ItemBoxAsset — Mystery Box Loot Tables
Overview
ItemBoxAsset extends ItemAsset and represents mystery box / loot box items. These are consumable items that, when used, destroy a cost from the player's inventory and generate random reward items from a configured drops table. The box supports two probability models (tier-weighted and equalized), two item origin modes (unbox to inventory vs. unwrap in world), and an optional bonus items system that can generate additional items beyond the base count.
In vanilla Unturned, mystery boxes are the primary mechanism for cosmetic item distribution, holiday event rewards, and premium loot crates. The probability model controls rarity distribution; the origin mode controls whether the items appear in the player's inventory or are dropped in the world as physical pickups.
Inheritance Chain
Asset
└── ItemAsset
└── ItemBoxAssetNo intermediate base classes. Mystery boxes are standard consumable items — they appear in the inventory, have a use action, and are consumed on use. The class name in code is ItemBoxAsset; the community often refers to them as "mystery boxes" or "loot boxes."
Enums
EBoxItemOrigin
csharp
public enum EBoxItemOrigin
{
Unbox,
Unwrap,
}Controls where generated items appear:
| Value | Behavior |
|---|---|
Unbox | Items are added directly to the player's inventory. If inventory is full, the item drops at the player's feet (standard force-give fallback). |
Unwrap | Items are dropped in the world at the player's position. The player must pick them up manually. This is the "unwrap" visual — items appear as physical pickups rather than magically appearing in inventory. |
Parsed from .dat key Item_Origin:
csharp
itemOrigin = p.data.ParseEnum("Item_Origin", defaultValue: EBoxItemOrigin.Unbox);Unbox is the default. Use Unwrap for gift-box-style items where the unwrapping animation shows the item appearing in the world.
EBoxProbabilityModel
csharp
public enum EBoxProbabilityModel
{
Original,
Equalized,
}Controls how drops are selected from the drops array:
| Model | Algorithm |
|---|---|
Original | Tier-weighted selection. Higher-rarity items have lower probability. Hardcoded tier weights: Legendary 5%, Epic 20%, Rare 75%. Within a tier, all items are equally likely. If the selected tier has no items, cascades down to the next available tier. |
Equalized | Flat selection. Every item in the drops array has equal probability regardless of its quality tier: 1.0 / drops.Length. |
Parsed from .dat key Probability_Model:
csharp
probabilityModel = p.data.ParseEnum("Probability_Model", defaultValue: EBoxProbabilityModel.Original);Original is the default for backwards compatibility with boxes made before Equalized was introduced.
Fields
Generate (_generate)
csharp
protected int _generate;
public int generate => _generate;The number of items to generate from the drops table when the box is opened. Each generation is an independent roll — generating 3 items means 3 separate selections from the drops table, each with its own probability roll.
Parsed from .dat key Generate:
csharp
_generate = p.data.ParseInt32("Generate");No default — if absent, ParseInt32 returns 0, meaning the box generates nothing. Always specify a positive generate count.
Destroy (_destroy)
csharp
protected int _destroy;
public int destroy => _destroy;The number of items to consume from the player's inventory as a cost before generating rewards. The items consumed depend on the box's configured cost item (typically a key or currency item), not on the _destroy field directly — _destroy is the count of cost items to remove.
Parsed from .dat key Destroy:
csharp
_destroy = p.data.ParseInt32("Destroy");If _destroy is 0, the box has no cost beyond consuming itself. If the player has fewer cost items than _destroy, only the available items are consumed — the surplus destroy count is silently skipped.
Drops (_drops)
csharp
protected int[] _drops;
public int[] drops => _drops;An array of item IDs representing the pool of possible drops. The array length is read from the .dat key Drops, and each element is read from Drop_N where N is the index:
csharp
_drops = new int[p.data.ParseInt32("Drops")];
for (int index = 0; index < drops.Length; index++)
{
drops[index] = p.data.ParseInt32("Drop_" + index);
}The order of items in the drops array matters for the probability models. In Original mode, items are grouped by their quality tier (determined by looking up each item's ItemAsset.rarity at generation time). In Equalized mode, the array index is the only factor — each index has equal weight.
Duplicate item IDs in the drops array increase that item's probability. In Equalized mode, an item appearing twice has twice the probability of an item appearing once. In Original mode, duplicates affect probability within the tier but not across tiers.
containsBonusItems
csharp
public bool containsBonusItems { get; protected set; }When true, additional items may be generated beyond _generate. The bonus probability and bonus count are defined by the game mode config, not by the asset. The asset only stores whether bonus items are possible.
Parsed from .dat key Contains_Bonus_Items:
csharp
containsBonusItems = p.data.ParseBool("Contains_Bonus_Items");Original Probability Model — Detailed Algorithm
The Original model uses a two-stage lottery:
Stage 1: Tier Selection
Three quality tiers have hardcoded weights:
Weight_Legendary = 0.05 (5%)
Weight_Epic = 0.20 (20%)
Weight_Rare = 0.75 (75%)
Total = 1.00These are static values — they are not configurable per-box and not stored in the asset. Every box using Original mode uses the same 5/20/75 split regardless of its drops composition.
The tier selection process:
- Generate a random float in
[0, 1). - If
random < 0.05: select Legendary tier. - Else if
random < 0.25(0.05 + 0.20): select Epic tier. - Else: select Rare tier (0.25 to 1.0, covering 75%).
If the selected tier has no items in the drops array (e.g., Legendary is selected but the drops list has no Legendary-rarity items), the selection cascades down:
- Try Legendary → if no items, try Epic.
- Try Epic → if no items, try Rare.
- Try Rare → if no items, try Legendary (wraps around).
- If the entire drops array is empty, no item is generated (silently skipped).
The cascade ensures a box always produces an item if at least one item exists in any tier. A box with only Common items still works — Legendary cascades down through Epic and Rare to Common.
Stage 2: Item Selection Within Tier
Within the selected tier, all items are equally likely:
probability = 1.0 / tierItemCountA random index within the tier's items is selected. The tier grouping happens at generation time by looking up each item ID's ItemAsset.rarity:
for each dropId in drops:
item = Assets.find(EAssetType.ITEM, dropId) as ItemAsset
tier = mapRarityToTier(item.rarity)
addToTierGroup(tier, dropId)The mapRarityToTier function is internal to the generation logic. The mapping between EItemRarity enum values and the three box tiers (Legendary/Epic/Rare) is:
EItemRarity.MYTHICAL→ Legendary tierEItemRarity.LEGENDARY→ Legendary tierEItemRarity.EPIC→ Epic tierEItemRarity.RARE→ Rare tierEItemRarity.UNCOMMON→ Rare tier (cascade target)EItemRarity.COMMON→ Rare tier (cascade target)
Items of Common or Uncommon rarity are mapped to the "Rare" tier for box probability purposes. This means a box with mostly Common items still uses the Rare tier's 75% weight, making Common items 15x more likely than Legendary items.
Example: Mixed Rarity Box
Drops list:
Drop_0 100 (Legendary sword)
Drop_1 101 (Epic armor)
Drop_2 102 (Epic helmet)
Drop_3 103 (Rare boots)
Drop_4 104 (Rare gloves)
Drop_5 105 (Common bandage)
Drop_6 106 (Common water)Tier grouping:
Legendary: [100] (1 item, 5% tier weight)
Epic: [101, 102] (2 items, 20% tier weight)
Rare: [103, 104, 105, 106] (4 items, 75% tier weight)Per-item probability:
Sword (100): 0.05 × 1.0 = 5.00%
Armor (101): 0.20 × 0.5 = 10.00%
Helmet (102): 0.20 × 0.5 = 10.00%
Boots (103): 0.75 × 0.25 = 18.75%
Gloves (104): 0.75 × 0.25 = 18.75%
Bandage (105): 0.75 × 0.25 = 18.75%
Water (106): 0.75 × 0.25 = 18.75%The Legendary sword is actually more likely per-item than any Rare item (5% vs 18.75% each), but the Epic items (10% each) fall between. This counterintuitive result occurs because the single Legendary item doesn't split its 5% share, while four Rare items split 75% (18.75% each), and two Epic items split 20% (10% each).
Equalized Probability Model — Detailed Algorithm
Every item in the drops array has equal probability:
probability = 1.0 / drops.LengthNo tier weighting. No cascading. The model is a simple uniform random selection over the drops array.
Example: Same Mixed Rarity Box
With Equalized model:
Sword (100): 1/7 = 14.29%
Armor (101): 1/7 = 14.29%
Helmet (102): 1/7 = 14.29%
Boots (103): 1/7 = 14.29%
Gloves (104): 1/7 = 14.29%
Bandage (105): 1/7 = 14.29%
Water (106): 1/7 = 14.29%Every item is equally likely regardless of rarity. The Legendary sword jumps from 5% to 14.29% — nearly triple its Original probability. The Rare items drop from 18.75% each to 14.29%.
Duplicate Entries
If a drops list contains duplicates:
Drop_0 100 (Legendary sword)
Drop_1 100 (Legendary sword again)
Drop_2 101 (Epic armor)In Equalized mode:
Sword: 2/3 = 66.67%
Armor: 1/3 = 33.33%The sword appears twice in the array and has twice the selection probability. This is the only way to weight items in Equalized mode.
In Original mode, the sword still appears once in the Legendary tier group (duplicates are deduplicated by ID within the tier grouping). The sword's probability remains 5% (one item in the 5% tier). Duplicates in Original mode have no effect beyond the first occurrence.
Unbox Flow
The full unbox sequence when a player uses a mystery box:
Step 1: Cost Validation
The system checks whether the player can afford the cost. The cost is typically a separate item (e.g., a key) that the box's use action requires. The _destroy field determines how many cost items are consumed.
If the player doesn't have enough cost items:
- The box use is rejected.
- An error message may be displayed.
- The box is not consumed.
Step 2: Cost Consumption
_destroy items are removed from the player's inventory:
- The system searches the player's inventory for items matching the cost item ID.
- Items are deleted starting from the first matching stack.
- If a stack has more items than needed, it's decremented.
- If a stack is fully consumed, the slot is cleared.
- If the total cost items found are fewer than
_destroy, only the available items are removed (no error, no blocking).
Step 3: Item Generation
For i = 0; i < _generate; i++:
- Select a random item from the drops table using the configured probability model.
- Look up the item ID in the asset registry:
Assets.find(EAssetType.ITEM, dropId). - If the item asset is null (invalid ID, missing asset), skip this generation silently.
- Create an
Iteminstance from the item asset withEItemOrigin.ADMIN(the origin tag indicates the item came from a box, not from crafting or world spawn). - Add the item to the player's inventory or drop in the world based on
itemOrigin. - If
itemOrigin == Unbox:ItemTool.tryForceGiveItem(player, itemId, 1)— adds to inventory, drops at feet if full. - If
itemOrigin == Unwrap:ItemManager.dropItem(item, player.transform.position, ...)— spawns a physical pickup at the player's position.
Step 4: Bonus Items
If containsBonusItems is true:
- The game mode config supplies a bonus probability (e.g., 10%).
- An independent random roll determines if a bonus is awarded.
- If the roll succeeds, an additional item is generated from the drops table using the same probability model.
- The bonus roll may repeat (configurable count) for multiple bonus items.
- The bonus system is entirely defined by game mode config — the asset only provides the
containsBonusItemsboolean gate.
Step 5: Box Consumption
The mystery box item stack decrements by 1. If the stack reaches 0, the item is removed from the inventory. The box is always consumed on use, regardless of whether any items were successfully generated.
Item Origin Tagging
Generated items are tagged with EItemOrigin.ADMIN:
csharp
Item item = new Item(itemAsset, EItemOrigin.ADMIN);This origin tag serves several purposes:
- Prevents the item from being used as a cost for the same box (anti-recursion).
- Tracks the item's provenance for economy analytics.
- Distinguishes box-generated items from crafted or world-spawned items.
- May affect tradeability in some game mode configs (box items may be non-tradeable).
The EItemOrigin enum includes values like WORLD, CRAFT, ADMIN, NATURE, etc. ADMIN is used for box items because the box is an admin-configured loot source — the server (admin) defines the box's contents, so the resulting items are admin-origin.
Description UI
ItemBoxAsset does not override BuildDescription. The box inherits the base ItemAsset tooltip, which shows:
- Item name and rarity color.
- Item description text.
- Size and weight.
- Tradeability.
Boxes do not display their contents, probability model, or generate/destroy counts in the tooltip. This is intentional — the contents are meant to be a mystery. Modders who want to display drop chances must implement a custom description override or use a plugin.
Performance Considerations
Asset Lookups During Generation
Each item generation calls Assets.find(EAssetType.ITEM, dropId), which is an O(log n) or O(1) dictionary lookup in the asset registry. For a box generating 10 items, this is 10 lookups — negligible overhead.
The tier-grouping step in Original mode calls Assets.find for every item in the drops array at the START of generation (to group by rarity). For a drops array with 100 items, this is 100 lookups per box open — still negligible but worth noting for boxes with very large drop pools on high-traffic servers.
Per-Box Item Creation
Each generated item creates a new Item instance (a class, heap-allocated). For a _generate of 10 and a box opened by 50 players simultaneously (e.g., a holiday event), that's 500 item allocations in a short window. The GC impact is minimal for typical values but scales linearly with player count and generate count.
Cargo Data Export
ItemBoxAsset does not override BuildCargoData. It inherits the base ItemAsset cargo export. The drops array, probability model, generate/destroy counts, and bonus flag are not exported to Cargo tables.
Modding Guide
Creating a Basic Mystery Box
ID 58200
ItemName "Supply Crate"
ItemDescription "Contains random supplies."
Rarity Rare
Size_X 2
Size_Y 2
Slot None
Generate 3
Destroy 0
Drops 5
Drop_0 100
Drop_1 101
Drop_2 102
Drop_3 103
Drop_4 104
Probability_Model Equalized
Item_Origin UnboxThis box generates 3 items from a pool of 5 items, each with equal (20%) probability. No cost to open beyond consuming the box itself. Items go to inventory.
Creating a Premium Crate (Original Model)
ID 58201
ItemName "Premium Crate"
Generate 2
Destroy 1
Drops 10
Drop_0 200
Drop_1 201
Drop_2 202
Drop_3 203
Drop_4 204
Drop_5 205
Drop_6 206
Drop_7 207
Drop_8 208
Drop_9 209
Probability_Model Original
Contains_Bonus_Items trueThis box:
- Generates 2 items using tier-weighted probability.
- Costs 1 key item to open.
- Has 10 possible drops.
- May grant bonus items beyond the 2 base.
Creating an Unwrap-Style Gift
ID 58202
ItemName "Holiday Gift"
Generate 1
Destroy 0
Drops 3
Drop_0 300
Drop_1 301
Drop_2 302
Item_Origin Unwrap
Probability_Model EqualizedThe item drops as a physical pickup in the world rather than appearing in inventory. The player must manually pick it up.
Common Pitfalls
Zero Generate: Omitting
Generate(or setting it to 0) means the box generates nothing. The box is consumed, the cost is paid, and nothing is received. Always set a positive generate count.Empty Drops: Omitting
Dropsor setting it to 0 means the drops array is empty. The box generates nothing — no item can be selected from an empty array. The box still consumes itself and the cost.Invalid Drop IDs: If a
Drop_Nvalue is an item ID that doesn't exist, the generation silently skips that slot. If ALL drops are invalid, the box generates nothing. Validate drop IDs against the asset registry at development time.Destroy exceeds availability: If
_destroyis 5 but the player only has 2 cost items, only 2 are consumed. The box still opens and generates items. Players can exploit this by intentionally carrying insufficient cost items.Probability misunderstanding: In
Originalmode, the 75% Rare tier includes Common and Uncommon items. A box of mostly Common items still gives each Common item ~75% / count probability, which may be higher than desired. UseEqualizedfor precise control.Duplicate drops in Original mode: Duplicating a drop entry does NOT increase its probability in
Originalmode (tier grouping deduplicates). UseEqualizedmode if you want weighted probabilities via duplication.Bonus items without game mode config: The
containsBonusItemsflag only enables the possibility. If the game mode config doesn't define bonus probabilities, no bonus items are ever generated. Test bonus behavior in the target game mode.
Advanced Probability Analysis
Expected Value per Box Opening
The expected number of items of each rarity per box opening can be calculated:
E[items_of_rarity] = generate * P(select_rarity)Where P(select_rarity) depends on the probability model:
Original Model:
P(Legendary) = 0.05 (if drops contain Legendary items, else cascades)
P(Epic) = 0.20 (if drops contain Epic items, else cascades)
P(Rare) = 0.75 (catch-all, includes Common/Uncommon)Equalized Model:
P(item_i) = 1.0 / drops.Length
P(rarity_R) = count_of_items_with_rarity_R / drops.LengthVariance and Streak Probability
With _generate independent rolls, the number of Legendary items follows a binomial distribution:
P(k Legendary in n rolls) = C(n,k) * p^k * (1-p)^(n-k)Where p = 0.05 for the Original model (if Legendary items exist in drops).
For a box with Generate 5 using the Original model:
P(0 Legendary) = C(5,0) * 0.05^0 * 0.95^5 = 0.7738 (77.4%)
P(1 Legendary) = C(5,1) * 0.05^1 * 0.95^4 = 0.2036 (20.4%)
P(2 Legendary) = C(5,2) * 0.05^2 * 0.95^3 = 0.0214 (2.1%)
P(3 Legendary) = C(5,3) * 0.05^3 * 0.95^2 = 0.0011 (0.1%)
P(4+ Legendary) = essentially negligibleThe 77.4% chance of zero Legendary items from 5 rolls explains why boxes feel "unlucky" — the majority of openings produce no top-tier items.
Streak Duration
The expected number of box openings to see at least one Legendary item:
E[openings_until_legendary] = 1 / P(at_least_one_legendary_per_box)For Generate 5 with p=0.05:
P(at_least_one) = 1 - 0.95^5 = 0.2262
E[openings] = 1 / 0.2262 = 4.42On average, a player opens ~4.4 boxes to see one Legendary item. The median is lower (~3 boxes) because the distribution is right-skewed. Some players open 10+ boxes with no Legendary — that's the ~8% tail of the distribution.
Cascade Effect on Probabilities
When a tier cascades, the probability shifts to the next available tier. Example with NO Legendary items in drops:
P(Legendary selected) → cascades to Epic
P(Epic) = 0.05 + 0.20 = 0.25 (Legendary + Epic weight)
P(Rare) = 0.75Example with NO Legendary or Epic items:
P(Legendary) → cascades to Epic → cascades to Rare
P(Rare) = 0.05 + 0.20 + 0.75 = 1.00 (everything cascades to Rare)The cascade ensures probability always sums to 1.0. No probability is lost.
Tier-by-Tier Drop Design Strategy
For modders designing box drop tables:
Fill all tiers: Ensure at least one item exists in each tier (Legendary, Epic, Rare). Empty tiers cause cascade, which inflates the probability of lower tiers.
Balance tier populations: Keep similar counts across tiers. 5 Legendary + 5 Epic + 5 Rare gives:
P(specific_Legendary) = 0.05 / 5 = 1.0% P(specific_Epic) = 0.20 / 5 = 4.0% P(specific_Rare) = 0.75 / 5 = 15.0%Use Equalized for flat odds: If every item should be equally likely regardless of rarity, use
Equalized. This is better for cosmetic boxes where rarity is cosmetic-only.Tier-exclusive drops: Some items only exist in one tier. A Legendary-only item has probability
0.05if it's the sole Legendary item, or0.05 / Nif Legendary has N items. This is much rarer than a Rare-only item with probability0.75 / N.
Statistical Testing for Drop Rates
To verify drop rates match intended probabilities, use a chi-squared test on box opening logs:
Expected count for item i = total_openings * generate * P(item_i)
Observed count for item i = actual drops recorded
chi_squared = sum((observed - expected)^2 / expected)With drops.Length - 1 degrees of freedom, check against the chi-squared distribution. A p-value below 0.05 suggests the actual distribution differs from the intended one (possible bug or probability model mismatch).
Bonus Item Probability
Game Mode Config Structure
The bonus item system relies on game mode config values:
BonusItemProbability: float (e.g., 0.10 = 10%)
MaxBonusItems: int (e.g., 3 = max 3 bonus items)
BonusItemRolls: int (e.g., 1 = one roll per opening)Bonus Calculation
When containsBonusItems is true:
For
i = 0; i < BonusItemRolls; i++:- Roll a random float in [0, 1).
- If
random < BonusItemProbability: generate one bonus item from drops. - Else: no bonus for this roll.
Multiple bonus rolls are independent. With
BonusItemRolls = 3andBonusItemProbability = 0.10:P(0 bonuses) = 0.9^3 = 0.729 P(1 bonus) = C(3,1) * 0.1 * 0.9^2 = 0.243 P(2 bonuses) = C(3,2) * 0.1^2 * 0.9 = 0.027 P(3 bonuses) = 0.1^3 = 0.001MaxBonusItemscaps the total bonus items regardless of rolls. IfMaxBonusItems = 1and two rolls succeed, only one bonus item is generated.
Bonus Item Origin
Bonus items use the same EItemOrigin.ADMIN origin as regular generated items. They are indistinguishable from regular drops — the player cannot tell which item was a bonus and which was a regular drop.
Economy Impact of Bonuses
Bonus items effectively increase the expected items per box opening by BonusItemRolls * BonusItemProbability. With 2 rolls at 10% probability, the expected bonus items per opening is 0.2 — one bonus every 5 boxes on average.
For an economy designer, the bonus system increases item output without changing the _generate field. This is useful for:
- Event bonuses: temporarily increase bonus probability during holidays.
- VIP perks: subscribers get higher bonus probability.
- First-time bonuses: new players get guaranteed bonus on their first box.
