Skip to content

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
    → ItemMagazineAsset

Class 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

FieldTypeBundle/.datDefaultDescription
_magazineGameObjectBundle "Magazine"Magazine attachment prefab
MaxAmountbyteInherited from ItemAssetMaximum ammo capacity (from Amount key)
shouldDeleteAtZeroAmountboolInherited from ItemAssetDelete 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)

FieldType.dat KeyDefaultDescription
_pelletsbytePellets1Number 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

FieldType.dat KeyDefaultDescription
_stuckbyteStuck0Chance 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

FieldTypeBundle/.datDescription
ProjectilePrefabOverrideGameObjectBundle "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

FieldType.dat KeyDefaultDescription
projectileDamageMultiplierfloatProjectile_Damage_Multiplier1.0Explosive projectile damage modifier
projectileBlastRadiusMultiplierfloatProjectile_Blast_Radius_Multiplier1.0Blast radius modifier
projectileLaunchForceMultiplierfloatProjectile_Launch_Force_Multiplier1.0Launch force modifier

Speed

FieldType.dat KeyDefaultDescription
_speedfloatSpeed1.0Projectile 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

FieldType.dat KeyDefaultDescription
_isExplosiveboolExplosive (flag)falseMagazine causes explosion on impact
_rangefloatRangeBlast radius in meters
explosionEffectGuid / _explosionGuid / ushortExplosionExplosion visual effect

Explosive Damage Tables

Field.dat KeyDescription
playerDamagePlayer_DamageDamage to players
zombieDamageZombie_DamageDamage to zombies
animalDamageAnimal_DamageDamage to animals
barricadeDamageBarricade_DamageDamage to barricades
structureDamageStructure_DamageDamage to structures
vehicleDamageVehicle_DamageDamage to vehicles
resourceDamageResource_DamageDamage to resources
objectDamageObject_DamageDamage to objects (defaults to resourceDamage)
explosionLaunchSpeedExplosion_Launch_SpeedPhysics launch force (defaults to playerDamage * 0.1)

Explosive Behavior Flags

Field.dat KeyDefaultDescription
ExplosionPlaysImpactEffectsExplosion_Plays_Impact_EffectstrueWhether surface effects appear
ExplosionPenetratesBuildablesExplosion_Penetrate_BuildablesfalseWhether blast penetrates constructions
spawnExplosionOnDedicatedServerSpawn_Explosion_On_Dedicated_ServerfalseForce 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 KeyDescription
tracerEffectGuid / _tracerTracerBullet tracer trail effect
_impactEffectGuid / _impactImpactBullet 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 KeyDefaultDescription
shouldFillAfterDetachShould_Fill_After_DetachfalseRefill 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

  1. Pellet minimum enforcement_pellets is clamped to a minimum of 1. A Pellets value of 0 in the .dat silently becomes 1. There is no way to create a magazine that fires zero pellets.
  2. Speed minimum enforcement_speed is clamped to a minimum of 0.01. Values below this threshold silently default to 1.0. Ultra-slow projectiles require a minimum Speed value of 0.01.
  3. Object_Damage fallback — If Object_Damage is not explicitly set, it defaults to Resource_Damage. Modders setting only Resource_Damage may be surprised to find objects taking the same damage.
  4. Explosive damage vs projectile damage — The explosive damage table (playerDamage etc.) is separate from the projectileDamageMultiplier. Both apply independently — magazine explosive damage multiplies the gun's base damage, then projectileDamageMultiplier applies on top.
  5. Stuck quality trackingshowQuality returns true only when stuck > 0. A magazine without a stuck chance never shows quality and the quality byte (offset 17) is unused.
  6. Delete empty deprecation — The deleteEmpty property is marked [Obsolete] and redirects to ItemAsset.ShouldDeleteAtZeroAmount. The Delete_Empty key still works but the recommended key is Should_Delete_At_Zero_Amount.