Bonus Drops

The lucky find in a broken block and the extra a killed mob is worth

Overview

A bonus drop is something extra a moment hands over on top of whatever the game already gives for it: the lucky find in a broken block, the bonus a killed mob is worth. New in 1.6.0: one table answers for both, because the question is the same either way - which row covers this name, how likely is the moment, and what does it hand over.

Nothing here replaces vanilla loot. A block still drops what it drops and a mob still drops what it drops; a bonus row spills its own reward beside that, at the broken block or at the corpse. A game update that re-tunes the base tables re-tunes them underneath you and your bonus keeps working.

Override configmods/mmoskilltree/bonus-drops.json

Shipped rows live in the jar and content packs ship theirs under Server/MMOSkillTree/BonusDrops/*.json. The owner file overrides any of them by name, one field at a time.

defaults<pack<owner

Replaces luck-loot.json and mob-drops.json

Both older files are folded into bonus-drops.json on the first start after upgrading, with every override carried across and the old files backed up. See What happens when you upgrade.

What a row looks like

A row is one file, and the filename is its name (its id). A pack ships rows at Server/MMOSkillTree/BonusDrops/<Name>.json; a server owner writes the same body into bonus-drops.json under that name.

// Server/MMOSkillTree/BonusDrops/Dragon_Hoard.json
{
  "When": { "Kind": "KillMob", "Match": "*Dragon*" },
  "Chance": {
    "Base": 5.0,
    "Factors": [ { "Factor": "hytale:stat", "Param": "MMO_Luck", "Weight": 0.2 } ],
    "Clamp": { "Min": 0.0, "Max": 20.0 }
  },
  "Loot": { "Rolls": [ { "Grants": { "DropLists": [ "My_Dragon_Hoard" ] } } ] },
  "Notify": { "Enabled": true }
}
FieldTypeDefaultDescription
When.Kind-optionalWhich moment the row answers for: BreakBlock when a player breaks a block, KillMob when a player kills a mob. A row naming no moment never fires and is named in the content audit.
When.Match-optionalWhich block or mob, as a name pattern (see Name patterns). Leave it out to cover everything in that moment.
Chance-optionalWhether the bonus moment fires at all, as a percent formula. Optional; each moment has its own default (see The odds).
Loot-optionalWhat the moment hands over: Lootables (shared table names), Rolls written inline, or both. A roll's Grants takes Items, DropLists (native item drop lists), Commands and Rewards.
Notify-optionalThe on-screen note when a bonus actually lands. Off unless the row asks for it, on both moments. See Telling the player.
Enabled-optionalfalse takes the row out of the table entirely.
* required

The four things a roll can hand over

  • DropLists spills a native item drop list where the moment happened, exactly the way the base game spills its own loot.
  • Items hands named items over, for an exact stack rather than a rolled table.
  • Commands runs console commands. Placeholders name what the moment knows: {player} and {block} on a break row, {player}, {mob} and {tier} on a kill row. {tier} resolves empty unless a companion mod attributes a rarity to the kill; the optional MMO Mob Scaling companion mod does, so with it installed a scaled kill's {tier} reads the mob's rarity (such as Legendary) and an unscaled kill's stays empty. A drop chance that weighs the mob is still written through its factors (see Reading what another mod knows), not this placeholder.
  • Rewards pays the player through the shared reward vocabulary (XP, currency, an item, a boost token, and anything another installed mod contributes).

Each roll inside Loot carries its own Chance and Conditions too, so one row can hold a common drop and a rare one side by side.

Only a player's kill counts

A mob that drowns or is killed by another mob drops its own loot and nothing from a KillMob row.

Name patterns

When.Match speaks the same name grammar the per-world Where block and a dialogue world scope speak, so a pattern means one thing everywhere. Case is ignored.

PatternMatchesReads as
Ore_CoalOre_Coal and nothing elseexactly
Ore_*Ore_Coal, Ore_Iron_Basaltstarting with
*_CrackedOre_Iron_Basalt_Crackedending with
*Trork*Trork, Trork_Chieftain, Zone1_Trork_Bruiseranywhere in the name
*everything in that momentthe catch-all

Where several rows match one name, the row pinning down the most characters wins: an exact pattern first, then the longest literal core, then the bare *. So *Trork* covers the family while a second row naming *Trork_Chieftain* takes over for that one mob, and neither file has to know about the other. Two equally specific patterns keep the first one folded. Anything more selective than a name belongs in a roll's own Conditions, never in the pattern.

The odds

Chance is a percent formula: a Base plus each factor's reading multiplied by its Weight, held inside an optional Clamp. Every number is a whole percent point.

"Chance": {
  "Base": 2.0,
  "Factors": [ { "Factor": "hytale:stat", "Param": "MMO_Luck", "Weight": 0.2 } ],
  "Clamp": { "Min": 0.0, "Max": 6.0 }
}

That reads as: 2% to start, rising with the killer's own Luck, never past 6%. Use MMO_Luck_MINING (or any other skill) as the Param to read a single skill's luck instead, and drop the Factors list entirely for a flat chance.

What an unwritten Chance means

Each moment carries its own default, so the two read the way an author expects:

MomentA row with no Chance
BreakBlockis as likely as the player's own summed luck - the MMO_Luck and MMO_Luck_<SKILL> channels the skill tree, mastery, gear and item enhancements all feed
KillMobalways fires, leaving the odds entirely to the rolls inside it

Reading what another mod knows

A chance formula (and a roll's Conditions) can weigh anything the installed mods publish. With the optional MMO Mob Scaling companion mod installed, five more readings become citable, all about the mob the moment happened to:

FactorReads
mmomobscaling:mob_rarity_tierthe mob's place on the rarity ladder: 0 for a plain mob, one step per authored tier
mmomobscaling:mob_rarityParam is a rarity name; 1 when the mob is exactly that tier, 0 otherwise
mmomobscaling:mob_affixParam is an affix name; 1 when the mob carries it, 0 otherwise
mmomobscaling:mob_difficultythe mob's frozen difficulty number
mmomobscaling:region_powerthe tracked player power in the region the moment happened in
{ "When": { "Kind": "KillMob", "Match": "*Trork*" },
  "Loot": { "Rolls": [
    { "Conditions": [ { "Factor": "mmomobscaling:mob_rarity_tier", "Min": 2 } ],
      "Chance": { "Base": 20.0,
                  "Factors": [ { "Factor": "mmomobscaling:mob_difficulty", "Weight": 2.0 } ],
                  "Clamp": { "Max": 60.0 } },
      "Grants": { "DropLists": [ "My_Elite_Trork_Hoard" ] } } ] } }

On a server without that mod nothing answers those names, so the condition stays shut and a formula term adds zero: one authored file is correct everywhere, with no dependency in either direction. /mmoconfig validate names a reading nothing answers to as a warning, so a typo is visible rather than silent.

A row that hands nothing over is a deliberate hole

Naming a pattern and granting no Loot is how "this one gives nothing" is said. Because the most specific pattern wins, an empty row for *Ore_Iron_Basalt_Cracked* stops a broader *Ore_Iron_* row covering that block, while every other iron variant keeps its bonus.

{ "When": { "Kind": "BreakBlock", "Match": "*Ore_Iron_Basalt_Cracked*" } }

That is different from "Enabled": false, which takes the row out of the table entirely and lets the broader pattern cover the block again.

Telling the player

Notify puts a short message on screen when a bonus actually lands. Leave it on for something rare and turn it off for anything frequent, or a bonus stops reading as one.

FieldTypeDefaultDescription
Enabled-optionalWhether the note shows at all. Off unless the row asks.
Color-optionalThe note's colour.
Icon-optionalThe icon shown beside it.
TitleKey / SubtitleKey-optionalTranslation keys, never literal text. Leave them out for the built-in wording. See Localization.
* required

Re-tuning a shipped row

Name the row in mods/mmoskilltree/bonus-drops.json and write only the parts you want changed; the body merges field by field over the shipped one.

{
  "schemaVersion": 1,
  "drops": {
    "Mmo_Trork_Kill": { "Enabled": false },
    "Mmo_Ore_Copper": { "Chance": { "Base": 2.0 } },
    "Mmo_Vortex_Charm": "__DISABLED__"
  }
}
  • {"Enabled": false} switches a row off without erasing its pattern.
  • A Loot block alone re-tunes what it hands over and leaves everything else as shipped.
  • The one-word "__DISABLED__" in place of the whole body is the shorthand for switching one off.
  • A name nothing ships decodes on its own, so you can also add a brand-new row from this file alone, with no content pack at all.

Precedence is the ordinary one: built-in defaults, then packs, then your owner file.

What ships in the box

A full set of rows ships with the mod, and every one of them is overridable by name:

  • Ten ore families (Mmo_Ore_Copper, Mmo_Ore_Iron, Mmo_Ore_Silver, Mmo_Ore_Gold, Mmo_Ore_Mithril, Mmo_Ore_Cobalt, Mmo_Ore_Thorium, Mmo_Ore_Adamantite, Mmo_Ore_Onyxium, Mmo_Ore_Prisma) - a lucky break hands over one extra ore, with no Chance block, so the odds are the breaker's own luck.
  • Four cracked-ore holes (Mmo_Ore_Iron_Basalt_Cracked, Mmo_Ore_Cobalt_Slate_Cracked, Mmo_Ore_Thorium_Mud_Cracked, Mmo_Ore_Adamantite_Magma_Cracked) - named, granting nothing, so those variants are carved out of their family row.
  • Mmo_Trork_Kill - a luck-scaled kill bonus, enabled by default. A server that wants it off writes {"drops": {"Mmo_Trork_Kill": {"Enabled": false}}}.
  • Mmo_Vortex_Charm - a rare charm from a gem block, with the full chance formula written out.

Each shipped file carries comments that double as the authoring tutorial for its half of the surface, so opening one in the jar is the fastest way to see a working row.

What happens when you upgrade

On the first start after upgrading, luck-loot.json and mob-drops.json are folded into bonus-drops.json automatically:

  • Every override is carried across. A row that named a drop still names it, a row that stated its own odds still states them, and a pattern switched off is still switched off.
  • Bare keys become anywhere-in-the-name patterns. An old key was a plain substring, so Trork is carried across as *Trork* to keep covering exactly what it covered.
  • One start-up line per converted row names what it became.
  • Both old files are backed up, moved into a timestamped backup/ copy rather than deleted, and a row already present in bonus-drops.json is never overwritten.
  • The shipped row ids are all one Mmo_ convention (Mmo_Ore_Adamantite, Mmo_Vortex_Charm, Mmo_Trork_Kill, and the rest). An owner override still keyed by a row's older id is re-filed under the id that row answers to now, before the file fold above even runs, with one start-up line per re-keyed row. If you have also written the current id yourself, that body wins and the older one is left in place with a line naming the clash, so nothing decides between your two versions for you.
  • The read-only _reference/defaults-luck-loot.json echo retires with them; the built-in luck rows are readable assets in the jar instead.

Packs need re-authoring

The old Server/MMOSkillTree/LuckLoot/ and Server/MMOSkillTree/MobDrops/ pack folders are gone, along with their LuckLoot and MobDrops pack control keys. A pack ships bonus rows at Server/MMOSkillTree/BonusDrops/ instead. The Migration Converter converts a pack's files for you, and /mmomigrate --action=pack reports what an installed pack still carries.

Checking your work

CommandWhat it does
/mmoconfig diff --args=luckLists your bonus-drop overrides by name, with the body you actually wrote. drops and all answer the same way.
/mmoconfig validateAudits the whole table (see the findings below).
/mmoconfig export / importThe config key is bonus-drops. Since 1.6.0 It replaced the old luck-loot key.
/mmoconfig reloadRe-reads the owner file without a restart.

The audit and the start-up check report:

  • a row naming no moment
  • an owner body that cannot be read
  • a pattern with a wildcard in the middle
  • a row-level chance that can never rise off zero
  • a factor name nothing answers to
  • a row that hands nothing over (as information - it may well be a deliberate hole)
  • two rows pinning down the same literal core in one moment

Trimming does not apply here

/mmoconfig trim strips redundant XP overrides only. The bonus-drop owner layer overrides a shipped row field by field rather than restating it whole, so there is nothing redundant for a trim to find.

Shipping rows in a content pack

Prefer a content pack: author one file per row under Server/MMOSkillTree/BonusDrops/ inside a pack folder, zip it, and drop the .zip into your server Mods/ folder. Building the zip.

mods/mmoskilltree/bonus-drops.json stays the quick per-server tweak that always wins.

A pack re-tunes another pack's row by re-authoring its name, or by naming a file with a top-level "Parent": "<name>" and restating one field.

Cross-pack composition happens through shared loot tables: a row's Loot.Lootables names a table under Server/ZiggfreedCommon/Lootables/, and a second pack enriches that table by authoring its own with "ContributesTo" pointing at it, never touching your files. Removing that pack removes exactly what it added.

// your pack
{ "When": { "Kind": "BreakBlock", "Match": "*Log_*" },
  "Loot": { "Lootables": [ "forest_finds" ] } }

// somebody else's pack, enriching it
{ "ContributesTo": "forest_finds",
  "Rolls": [ { "Chance": { "Base": 25.0 },
               "Grants": { "Items": [ { "Item": "Ingredient_Resin" } ] } } ] }

(Base is a percent, so 25.0 is a one-in-four resin drop; leave the Chance group off entirely for a roll that always fires. The roll shape itself - Conditions, Chance, Grants, Ladder, Cue - is documented once on Shared Schemas.)

Full authoring reference: Content pack asset types and the shared library's content paths. When you only want to retune a single shipped row on your own server, mods/mmoskilltree/bonus-drops.json is the quick-tweak alternative to a pack.

  • Mob Drop Commands - the simpler kill-only surface, still separate, consulted first for a kill.
  • Skill trees - where a player's luck comes from.
  • Elite mobs - what replaced the removed built-in tier system, and how to gate a bonus on it.