ItemMagazineAsset — Magazine Attachments
ItemMagazineAsset defines magazine attachments that provide ammunition, alter projectile behavior, and can carry explosive payloads. At 293 lines, it is the third-largest attachment class behind ItemTacticalAsset and ItemSightAsset. It inherits from ItemCaliberAsset for stat modifiers and adds pellet count (shotgun), stuck chance, explosive damage tables, projectile overrides, tracer effects, impact effects, and projectile speed.
Source code location: Unturned/Bundles/ItemMagazineAsset.cs
Inheritance Chain
ItemAsset
→ ItemCaliberAsset
→ ItemMagazineAssetClass Definition
csharp
public class ItemMagazineAsset : ItemCaliberAsset
{
protected GameObject _magazine;
public GameObject magazine => _magazine;
public GameObject ProjectilePrefabOverride { get; set; }
private byte _pellets;
public byte pellets => _pellets;
private byte _stuck;
public byte stuck => _stuck;
protected float _range;
public float range => _range;
public float projectileDamageMultiplier { get; protected set; }
public float projectileBlastRadiusMultiplier { get; protected set; }
public float projectileLaunchForceMultiplier { get; protected set; }
public float ProjectileLifespanOverride { get; set; }
public float playerDamage, zombieDamage, animalDamage;
public float barricadeDamage, structureDamage, vehicleDamage;
public float resourceDamage, objectDamage;
public float explosionLaunchSpeed;
public bool ExplosionPlaysImpactEffects { get; set; } = true;
public bool ExplosionPenetratesBuildables { get; set; } = false;
public Guid explosionEffectGuid;
private ushort _explosion;
public ushort explosion => _explosion;
public Guid tracerEffectGuid;
private ushort _tracer;
private Guid _impactEffectGuid;
private ushort _impact;
public override bool showQuality => stuck > 0;
private float _speed;
public float speed => _speed;
protected bool _isExplosive;
public bool isExplosive => _isExplosive;
public bool shouldFillAfterDetach { get; protected set; }
}Core Fields
Prefab and Ammo
| Field | Type | Bundle/.dat | Default | Description |
|---|---|---|---|---|
_magazine | GameObject | Bundle "Magazine" | — | Magazine attachment prefab |
MaxAmount | byte | Inherited from ItemAsset | — | Maximum ammo capacity (from Amount key) |
shouldDeleteAtZeroAmount | bool | Inherited from ItemAsset | — | Delete magazine when empty |
The MaxAmount field is inherited from ItemAsset and defines the maximum rounds the magazine holds. This is displayed in the gun's ammo line as currentAmmo / maxAmount.
Pellets (Shotgun)
| Field | Type | .dat Key | Default | Description |
|---|---|---|---|---|
_pellets | byte | Pellets | 1 | Number of pellets per shot |
csharp
_pellets = p.data.ParseUInt8("Pellets");
if (pellets < 1)
_pellets = 1;The pellets value is clamped to a minimum of 1. A value of 5 creates a shotgun blast with 5 simultaneous projectiles per trigger pull. Each pellet follows the standard ballistic path and deals independent damage.
Stuck Chance
| Field | Type | .dat Key | Default | Description |
|---|---|---|---|---|
_stuck | byte | Stuck | 0 | Chance to get stuck when empty (reload required) |
The showQuality override returns true when stuck > 0, enabling quality tracking for magazines that can malfunction. A stuck magazine requires a reload to clear.
Projectile Override
| Field | Type | Bundle/.dat | Description |
|---|---|---|---|
ProjectilePrefabOverride | GameObject | Bundle "Projectile" | Overrides the gun's projectile prefab |
csharp
ProjectilePrefabOverride = p.bundle.load<GameObject>("Projectile");If the magazine's bundle contains a "Projectile" prefab, it replaces the gun's own projectile. This allows magazines to completely change the weapon's projectile type (e.g., switching from hitscan to rocket, or changing the projectile model). The optional ProjectileLifespanOverride field extends or reduces the projectile's lifetime.
Projectile Multipliers
| Field | Type | .dat Key | Default | Description |
|---|---|---|---|---|
projectileDamageMultiplier | float | Projectile_Damage_Multiplier | 1.0 | Explosive projectile damage modifier |
projectileBlastRadiusMultiplier | float | Projectile_Blast_Radius_Multiplier | 1.0 | Blast radius modifier |
projectileLaunchForceMultiplier | float | Projectile_Launch_Force_Multiplier | 1.0 | Launch force modifier |
Speed
| Field | Type | .dat Key | Default | Description |
|---|---|---|---|---|
_speed | float | Speed | 1.0 | Projectile speed multiplier |
csharp
_speed = p.data.ParseFloat("Speed");
if (speed < 0.01f)
_speed = 1.0f;The speed is clamped to a minimum of 0.01, with values below that threshold defaulting to 1.0.
Explosive Magazine System
Magazines can be explosive, dealing area damage independently of the gun's projectile system. This is most commonly used for under-barrel grenade launcher ammunition and explosive shotgun shells.
Explosive Configuration
| Field | Type | .dat Key | Default | Description |
|---|---|---|---|---|
_isExplosive | bool | Explosive (flag) | false | Magazine causes explosion on impact |
_range | float | Range | — | Blast radius in meters |
explosionEffectGuid / _explosion | Guid / ushort | Explosion | — | Explosion visual effect |
Explosive Damage Tables
| Field | .dat Key | Description |
|---|---|---|
playerDamage | Player_Damage | Damage to players |
zombieDamage | Zombie_Damage | Damage to zombies |
animalDamage | Animal_Damage | Damage to animals |
barricadeDamage | Barricade_Damage | Damage to barricades |
structureDamage | Structure_Damage | Damage to structures |
vehicleDamage | Vehicle_Damage | Damage to vehicles |
resourceDamage | Resource_Damage | Damage to resources |
objectDamage | Object_Damage | Damage to objects (defaults to resourceDamage) |
explosionLaunchSpeed | Explosion_Launch_Speed | Physics launch force (defaults to playerDamage * 0.1) |
Explosive Behavior Flags
| Field | .dat Key | Default | Description |
|---|---|---|---|
ExplosionPlaysImpactEffects | Explosion_Plays_Impact_Effects | true | Whether surface effects appear |
ExplosionPenetratesBuildables | Explosion_Penetrate_Buildables | false | Whether blast penetrates constructions |
spawnExplosionOnDedicatedServer | Spawn_Explosion_On_Dedicated_Server | false | Force explosion on dedicated server |
Effect Resolution
The magazine provides helper methods for effect asset resolution:
csharp
public bool IsExplosionEffectRefNull()
{
return explosion == 0 && explosionEffectGuid.IsEmpty();
}
public EffectAsset FindExplosionEffect()
{
return Assets.FindEffectAssetByGuidOrLegacyId(explosionEffectGuid, explosion);
}Tracer and Impact Effects
| Field | .dat Key | Description |
|---|---|---|
tracerEffectGuid / _tracer | Tracer | Bullet tracer trail effect |
_impactEffectGuid / _impact | Impact | Bullet impact effect (overrides gun's default) |
The tracer effect creates a visible trail behind each bullet. The impact effect replaces the gun's default impact particle. Helper methods resolve these via dual GUID/legacy ID lookup:
csharp
public EffectAsset FindTracerEffectAsset()
{
return Assets.FindEffectAssetByGuidOrLegacyId(tracerEffectGuid, _tracer);
}
public EffectAsset FindImpactEffectAsset()
{
return Assets.FindEffectAssetByGuidOrLegacyId(_impactEffectGuid, _impact);
}Should Fill After Detach
| Field | .dat Key | Default | Description |
|---|---|---|---|
shouldFillAfterDetach | Should_Fill_After_Detach | false | Refill ammo capacity when detached |
When true, the magazine's ammo count is automatically refilled to MaxAmount when removed from a gun. This is used for magazines that recharge or regenerate ammunition.
PopulateAsset
csharp
public override void PopulateAsset(in PopulateAssetParameters p)
{
base.PopulateAsset(in p);
_magazine = loadRequiredAsset<GameObject>(p.bundle, "Magazine");
ProjectilePrefabOverride = p.bundle.load<GameObject>("Projectile");
_pellets = p.data.ParseUInt8("Pellets");
if (pellets < 1) _pellets = 1;
_stuck = p.data.ParseUInt8("Stuck");
projectileDamageMultiplier = p.data.ParseFloat("Projectile_Damage_Multiplier", 1.0f);
projectileBlastRadiusMultiplier = p.data.ParseFloat("Projectile_Blast_Radius_Multiplier", 1.0f);
projectileLaunchForceMultiplier = p.data.ParseFloat("Projectile_Launch_Force_Multiplier", 1.0f);
_range = p.data.ParseFloat("Range");
playerDamage = p.data.ParseFloat("Player_Damage");
zombieDamage = p.data.ParseFloat("Zombie_Damage");
animalDamage = p.data.ParseFloat("Animal_Damage");
barricadeDamage = p.data.ParseFloat("Barricade_Damage");
structureDamage = p.data.ParseFloat("Structure_Damage");
vehicleDamage = p.data.ParseFloat("Vehicle_Damage");
resourceDamage = p.data.ParseFloat("Resource_Damage");
explosionLaunchSpeed = p.data.ParseFloat("Explosion_Launch_Speed", playerDamage * 0.1f);
ExplosionPlaysImpactEffects = p.data.ParseBool("Explosion_Plays_Impact_Effects", true);
ExplosionPenetratesBuildables = p.data.ParseBool("Explosion_Penetrate_Buildables");
_explosion = p.data.ParseGuidOrLegacyId("Explosion", out explosionEffectGuid);
if (p.data.ContainsKey("Object_Damage"))
objectDamage = p.data.ParseFloat("Object_Damage");
else
objectDamage = resourceDamage;
_tracer = p.data.ParseGuidOrLegacyId("Tracer", out tracerEffectGuid);
_impact = p.data.ParseGuidOrLegacyId("Impact", out _impactEffectGuid);
_speed = p.data.ParseFloat("Speed");
if (speed < 0.01f) _speed = 1.0f;
_isExplosive = p.data.ContainsKey("Explosive");
spawnExplosionOnDedicatedServer = p.data.ContainsKey("Spawn_Explosion_On_Dedicated_Server");
shouldFillAfterDetach = p.data.ParseBool("Should_Fill_After_Detach", false);
}BuildDescription
BuildDescription shows pellet count, explosive properties, and caliber-level stat modifiers:
csharp
public override void BuildDescription(ItemDescriptionBuilder builder, Item itemInstance)
{
base.BuildDescription(builder, itemInstance);
if (!builder.HasFlag(EItemDescriptionFlags.Uncategorized))
return;
if (_pellets > 1)
builder.Append(localization.format("ItemDescription_PelletCount", _pellets), ...);
if (isExplosive)
{
// Explosive bullet indicator
// Blast radius
// Damage per entity type (player, zombie, animal, barricade,
// structure, vehicle, resource, object)
}
}Cargo Data Export
csharp
CargoDeclaration data = builder.GetOrAddDeclaration("Magazine");
data.Append("GUID", GUID);
data.Append("Pellets", pellets);
data.Append("Stuck", stuck);
data.Append("Projectile_Damage_Multiplier", projectileDamageMultiplier);
data.Append("Projectile_Blast_Radius_Multiplier", projectileBlastRadiusMultiplier);
data.Append("Projectile_Launch_Force_Multiplier", projectileLaunchForceMultiplier);
data.Append("Range", range);
data.Append("Player_Damage", playerDamage);
// ... all other damage fields ...
data.Append("Explosion", explosion);
data.Append("Speed", speed);
data.Append("Explosive", isExplosive);
data.Append("Should_Fill_After_Detach", shouldFillAfterDetach);Inherited Stat Modifiers
As with all ItemCaliberAsset subclasses, magazines inherit the full stat modifier system (recoil_x, recoil_y, spread, sway, shake, FirerateOffset, ballisticDamageMultiplier, BallisticGravityMultiplier, aimDurationMultiplier, aimingRecoilMultiplier, aimingMovementSpeedMultiplier). These allow magazines to affect weapon handling beyond just ammunition — for example, a drum magazine might increase sway or reduce ADS speed due to its weight.
Common Issues
- Pellet minimum enforcement —
_pelletsis clamped to a minimum of 1. APelletsvalue of 0 in the.datsilently becomes 1. There is no way to create a magazine that fires zero pellets. - Speed minimum enforcement —
_speedis clamped to a minimum of 0.01. Values below this threshold silently default to 1.0. Ultra-slow projectiles require a minimumSpeedvalue of 0.01. - Object_Damage fallback — If
Object_Damageis not explicitly set, it defaults toResource_Damage. Modders setting onlyResource_Damagemay be surprised to find objects taking the same damage. - Explosive damage vs projectile damage — The explosive damage table (
playerDamageetc.) is separate from theprojectileDamageMultiplier. Both apply independently — magazine explosive damage multiplies the gun's base damage, thenprojectileDamageMultiplierapplies on top. - Stuck quality tracking —
showQualityreturns true only whenstuck > 0. A magazine without a stuck chance never shows quality and the quality byte (offset 17) is unused. - Delete empty deprecation — The
deleteEmptyproperty is marked[Obsolete]and redirects toItemAsset.ShouldDeleteAtZeroAmount. TheDelete_Emptykey still works but the recommended key isShould_Delete_At_Zero_Amount.
