ItemTacticalAsset — Tactical Attachments
ItemTacticalAsset defines utility attachments that mount to the gun's tactical rail. It supports four independent functions — laser sights, flashlights, rangefinders, and bayonet melee attacks — which can be combined on a single attachment. It inherits from ItemCaliberAsset for stat modifiers and caliber compatibility. At 267 lines, it is the largest attachment class due to the full melee damage system embedded within it.
Source code location: Unturned/Bundles/ItemTacticalAsset.cs
Inheritance Chain
ItemAsset
→ ItemCaliberAsset
→ ItemTacticalAssetClass Definition
csharp
public class ItemTacticalAsset : ItemCaliberAsset
{
protected GameObject _tactical;
public GameObject tactical => _tactical;
private bool _isLaser;
public bool isLaser => _isLaser;
private bool _isLight;
public bool isLight => _isLight;
public PlayerSpotLightConfig lightConfig { get; protected set; }
private bool _isRangefinder;
public bool isRangefinder => _isRangefinder;
private bool _isMelee;
public bool isMelee => _isMelee;
public ItemTacticalAssetMeleeProperties MeleeProperties { get; set; }
public Color laserColor { get; protected set; }
}Core Function Flags
Laser Sight
| Field | .dat Key | Description |
|---|---|---|
_isLaser | Laser (flag) | Projects a visible laser sight line |
laserColor | Laser_Color | Laser beam color (default red) |
The laser color is parsed using LegacyParseColor which accepts named colors or RGB values:
csharp
Color _laserColor = p.data.LegacyParseColor("Laser_Color", Color.red);
_laserColor = MathfEx.Clamp01(_laserColor);
_laserColor.a = 1.0f;
laserColor = _laserColor;The color is clamped to [0,1] range and alpha is forced to 1.0 (fully opaque). This means transparent lasers are not possible through the color picker — the alpha channel is always overwritten.
Flashlight
| Field | .dat Key | Description |
|---|---|---|
_isLight | Light (flag) | Mounted flashlight |
lightConfig | PlayerSpotLightConfig (nested key set) | Flashlight properties |
When isLight is true, a PlayerSpotLightConfig is created from the .dat data:
csharp
if (isLight)
{
lightConfig = new PlayerSpotLightConfig(p.data);
}PlayerSpotLightConfig reads nested keys for spotlight range, angle, intensity, and color. The flashlight is toggled by the player's tactical key bind.
Rangefinder
| Field | .dat Key | Description |
|---|---|---|
_isRangefinder | Rangefinder (flag) | Distance measurement |
When active, the rangefinder displays the distance to whatever the player is aiming at. The maximum range is the gun's rangeRangefinder value (which defaults to the weapon's range).
Melee / Bayonet
| Field | .dat Key | Description |
|---|---|---|
_isMelee | Melee (flag) | Bayonet-style melee attachment |
MeleeProperties | Nested keys | Full melee damage configuration |
When isMelee is true, a set of melee-specific properties is loaded. The melee attack activates when the player uses the tactical attachment as a melee weapon. This is independent of the gun's melee system and uses its own damage multipliers.
ItemTacticalAssetMeleeProperties
The melee properties class provides a complete standalone melee damage system within the tactical attachment:
csharp
public class ItemTacticalAssetMeleeProperties
{
public float MeleeRange { get; set; }
public PlayerDamageMultiplier MeleePlayerDamageMultiplier { get; set; }
public DamagePlayerParameters.Bleeding MeleePlayerDamageBleeding { get; set; }
public DamagePlayerParameters.Bones MeleePlayerDamageBones { get; set; }
public ZombieDamageMultiplier MeleeZombieDamageMultiplier { get; set; }
public EZombieStunOverride MeleeZombieStunOverride { get; set; }
public AnimalDamageMultiplier MeleeAnimalDamageMultiplier { get; set; }
public float MeleeZombieRagdollForceMultiplier { get; set; }
}Melee Damage Configuration
| Field | .dat Key | Default | Description |
|---|---|---|---|
MeleeRange | Melee_Range | 2.0 | Bayonet reach in meters |
MeleePlayerDamageMultiplier | Melee_Player_Damage + limb multipliers | 40 base | Player damage per body part |
MeleePlayerDamageBleeding | Melee_Player_Damage_Bleeding | None | Whether bayonet causes bleeding |
MeleePlayerDamageBones | Melee_Player_Damage_Bones | None | Whether bayonet breaks bones |
MeleeZombieDamageMultiplier | Melee_Zombie_Damage + limb multipliers | 40 base | Zombie damage per body part |
MeleeZombieStunOverride | Melee_Stun_Zombie_Always/Never | None | Force or prevent zombie stun |
MeleeAnimalDamageMultiplier | Melee_Animal_Damage + limb multipliers | 40 base | Animal damage per body part |
MeleeZombieRagdollForceMultiplier | Melee_Zombie_Ragdoll_Force_Multiplier | 1.0 | Knockback force on zombies |
The damage multipliers follow the same pattern as ItemWeaponAsset with per-limb multipliers. The limb multiplier defaults differ between player (arm=0.6, leg=0.6, spine=0.8, skull=1.1) and zombie/animal (leg=0.3, arm=0.3, spine=0.6, skull=1.1).
Zombie Stun Override
csharp
public enum EZombieStunOverride { None, Always, Never }| Key | Effect |
|---|---|
Melee_Stun_Zombie_Always | Always stuns zombies |
Melee_Stun_Zombie_Never | Never stuns zombies |
| (neither) | Uses default stun behavior |
Game Mode Config Compatibility
The MeleeAnimalOrPlayerDamageMultiplier and MeleeZombieOrPlayerDamageMultiplier properties select between animal and player damage based on the game mode config:
csharp
public IDamageMultiplier MeleeAnimalOrPlayerDamageMultiplier
{
get
{
bool usePlayerDmg = Provider.modeConfigData.Animals.Weapons_Use_Player_Damage;
return usePlayerDmg ? MeleePlayerDamageMultiplier : MeleeAnimalDamageMultiplier;
}
}This allows servers to configure whether animals take player-equivalent damage from bayonet attacks.
Damage Parameter Initialization
The melee properties can initialize damage parameters for the damage system:
csharp
public void InitPlayerDamageParameters(ref DamagePlayerParameters parameters)
{
parameters.bleedingModifier = MeleePlayerDamageBleeding;
parameters.bonesModifier = MeleePlayerDamageBones;
}Melee Description UI
The BuildDescription method renders a full melee damage description including:
- Melee range.
- Player damage per limb (head, body, arm, leg).
- Bleeding and bone break modifiers.
- Zombie damage per limb.
- Animal damage per limb.
PopulateAsset
csharp
public override void PopulateAsset(in PopulateAssetParameters p)
{
base.PopulateAsset(in p);
_tactical = loadRequiredAsset<GameObject>(p.bundle, "Tactical");
_isLaser = p.data.ContainsKey("Laser");
_isLight = p.data.ContainsKey("Light");
if (isLight)
lightConfig = new PlayerSpotLightConfig(p.data);
_isRangefinder = p.data.ContainsKey("Rangefinder");
_isMelee = p.data.ContainsKey("Melee");
if (_isMelee)
{
MeleeProperties = new ItemTacticalAssetMeleeProperties();
MeleeProperties.PopulateAsset(in p);
}
Color _laserColor = p.data.LegacyParseColor("Laser_Color", Color.red);
_laserColor = MathfEx.Clamp01(_laserColor);
_laserColor.a = 1.0f;
laserColor = _laserColor;
}All four function flags are parsed as key presence (ContainsKey). Any combination can be active simultaneously — a tactical attachment could be a laser, flashlight, rangefinder, and bayonet all at once.
BuildDescription
BuildDescription delegates to MeleeProperties.BuildDescription when the melee flag is set:
csharp
public override void BuildDescription(ItemDescriptionBuilder builder, Item itemInstance)
{
base.BuildDescription(builder, itemInstance);
if (!builder.HasFlag(EItemDescriptionFlags.Uncategorized))
return;
if (MeleeProperties != null)
MeleeProperties.BuildDescription(builder);
}The base class (ItemCaliberAsset.BuildDescription) renders all stat modifiers. The melee properties add their damage breakdown separately.
Cargo Data Export
csharp
CargoDeclaration data = builder.GetOrAddDeclaration("Tactical");
data.Append("GUID", GUID);
data.Append("Laser", isLaser);
data.Append("Light", isLight);
data.Append("Rangefinder", isRangefinder);
data.Append("Melee", isMelee);
data.Append("Laser_Color", laserColor);Attachment Integration
When a tactical attachment is equipped, UseableGun activates each enabled function:
- Laser: A
LineRendererfrom the tactical hook to the aim point. The color islaserColor. Visible to all players. - Light: A
PlayerSpotLightConfigspotlight is enabled from the tactical hook. Can be toggled on/off. - Rangefinder: The HUD displays distance to the targeted point, up to the gun's
rangeRangefinder. - Melee: The attachment provides an alternate melee attack with its own damage values. This replaces or augments the gun's standard melee.
The _tactical prefab is instantiated and parented to the weapon's Hook_Tactical transform.
Common Issues
- Tactical melee stacking — The
ItemTacticalAssetMeleePropertiesprovides a separate melee attack that activates when the tactical attachment is used as a melee weapon. This is independent of the gun's melee system and uses its own damage multipliers. - Laser color alpha override — Alpha is forced to 1.0 regardless of what value is parsed. This means
Laser_Colorvalues with alpha channels are silently clamped. Transparent or semi-transparent laser beams are not possible. - Light without Laser — A tactical attachment with
Lightbut notLaserhas a separate toggle key. Players may not realize the flashlight is available if the laser (which has a visible indicator) is absent. - Melee damage defaults — The default melee damage values (40 base) are the same as
ItemMeleeAssetdefaults. If specific values are not set in the.dat, the bayonet may deal unexpected damage amounts. - All four flags active — A tactical attachment with all four functions (laser, light, rangefinder, melee) may have conflicting key bindings or UI overload. The game allows it but the player experience may be confusing.
