Weapons · Updated Mar 23, 2026
Weapon Actor
ASLWeaponActor is the persistent world actor that represents a single weapon instance. It owns two things:
#Overview
ASLWeaponActor is the persistent world actor that represents a single weapon instance. It owns two things:
- Config — a
USLWeaponDataAssetreference that defines fire modes, meshes, montages, and all tuning
- State —
CurrentAmmo, a replicatedint32that persists across equip/unequip cycles and survives a drop
Every weapon in the game is an ASLWeaponActor in the world at all times. Characters carry weapon actors — hidden when not equipped, visible when equipped. When a weapon is dropped it is simply detached and left in the world, retaining whatever ammo it had.
#Lifecycle
Spawn (e.g. pickup placed in level, or spawned by game logic)
└── BeginPlay (authority) → CurrentAmmo = GetMaxAmmo()
Pickup / Equip
└── RequestEquip(WeaponActor) [server RPC on ASLCharacterBase]
└── WeaponsComponent::SetPendingEquipWeapon(WeaponActor)
└── Reads WeaponActor->WeaponData for abilities, equip config
└── FinalizeEquip sets EquippedWeapon (replicated)
└── Weapon actor hidden/attached to character
During play (equipped)
└── USLAmmoAttributeSet.CurrentAmmo is the live ammo counter (GAS attribute on the ASC)
└── Each shot applies AmmoDecrementEffect GE on authority → CurrentAmmo decrements
└── Attribute replicates → OnAmmoChanged delegate on USLWeaponsComponent fires on all clients
└── WeaponActor->CurrentAmmo is NOT updated per-shot — it holds the value from equip time
Unequip (weapon swap)
└── WeaponsComponent::UnequipWeapon
└── EquippedWeapon set to nullptr (previous actor stays in the character's carried inventory)
└── Actor hidden, ammo state preserved
Drop (Server_DropWeapon → OnWeaponDropped)
└── WeaponsComponent::UnequipWeapon — removes fire abilities, hides mesh
└── RemoveWeaponFromInventory — removed from CarriedWeapons
└── ASLWeaponPickup spawned at DropSpawnOffset from character (default: 40 fwd, 50 up)
└── SetWeapon(WeaponActor) — pickup takes ownership, CurrentAmmo is already correct
└── LaunchPickup(ForwardVector) — arc with random spread, 2 bounces, then becomes collectible
└── OnWeaponDropped(DroppedPickup) — BlueprintNativeEvent, override for drop FX/animation
#Ammo Model
SystemLink uses a single ammo pool per weapon. There is no reserve — CurrentAmmo is the only counter. Pickups add directly to CurrentAmmo, clamped to MaxAmmo.
| Concept | Where |
|---|---|
MaxAmmo | Defined on FSLWeaponFireMode on the weapon data asset |
CurrentAmmo | Replicated int32 on ASLWeaponActor |
| Decrement | AmmoDecrementEffect GE applied on authority per shot via USLGameplayAbility_Fire |
| Increment | Via SetCurrentAmmo — called by pickup actors or game logic |
| Initialization | BeginPlay on authority sets CurrentAmmo = GetMaxAmmo() |
#API
| Function | Authority | Purpose |
|---|---|---|
SetCurrentAmmo(int32 NewAmmo) | Server | Sets CurrentAmmo, clamped to [0, MaxAmmo]. Call from pickup logic or ammo grants. |
GetMaxAmmo() | Any | Returns WeaponData->PrimaryFireMode.MaxAmmo. Used for clamping and initialization. |
OnRep_CurrentAmmo() | Client | Called automatically when CurrentAmmo replicates. Not used for HUD — the HUD binds to USLWeaponsComponent.OnAmmoChanged instead. |
OnAttachedToCharacter(Character) | Local client | Called by USLWeaponsComponent after mesh attachment. Implement in BP to set up DMIs, bind delegates, cache character reference. |
OnDetachedFromCharacter() | Local client | Called by USLWeaponsComponent on unequip. Clear DMI references and any other character-tied state. |
OnWeaponFired(MuzzleLoc, FireDir) | Local client | Called by USLGameplayAbility_Fire each shot. Drive muzzle flash, material pulses, per-shot sounds. |
OnWeaponStoppedFiring() | Local client | Called by USLGameplayAbility_Fire on EndAbility. Stop looping effects, reset material state. |
#Carried Inventory
Characters carry multiple weapon actors. Only the equipped actor drives the USLWeaponsComponent (meshes, fire abilities, ammo attribute). Unequipped actors are kept hidden — their ammo is preserved in CurrentAmmo without any attribute set involvement.
When the player swaps weapons the incoming actor's CurrentAmmo is pushed into USLAmmoAttributeSet on equip, and the outgoing actor's attribute value is written back to its CurrentAmmo before being hidden. This keeps each weapon's ammo independent across swaps.
#Spawning
Weapon actors can be:
- Placed in the level — set
WeaponDatain the Details panel.CurrentAmmoinitializes fromMaxAmmoon BeginPlay.
- Spawned at runtime —
SpawnActor<ASLWeaponActor>, then setWeaponDatabefore or immediately after spawning. CallSetCurrentAmmoif you want a specific count other than full.
SpawnActor → ASLWeaponActor
Set WeaponData = DA_AssaultRifle
(BeginPlay initializes CurrentAmmo = MaxAmmo automatically)
#Infinite Ammo
Infinite ammo bypasses the decrement entirely. Three layers, checked in priority order:
| Layer | Mechanism | Use case |
|---|---|---|
| Global | Bool on SLGameModeBase.bInfiniteAmmo | Game mode (e.g. dev mode, infinite ammo playlist) |
| Per-character | SLTags.States.Weapon.InfiniteAmmo tag on the ASC, applied via duration-based GE | Powerup pickup — expires automatically when the GE duration ends |
| Per fire mode | bInfiniteAmmo bool on FSLWeaponFireMode | Weapons that are always infinite (e.g. melee placeholder) |
When any layer is active, the ammo decrement GE is skipped and firing is not blocked at zero ammo.
Not yet implemented. The bInfiniteAmmo flags and tag checks are a future milestone. The decrement GE itself is live.
#Blueprint Subclassing
ASLWeaponActor is BlueprintType. Create a BP subclass (e.g. BP_WeaponActor_AssaultRifle) to:
- Set
WeaponDataas a default value so the actor is self-contained when placed in the level
- Implement
OnAttachedToCharacter(Character)— receive the character reference, get the weapons component and meshes from it, create DMIs, bind toOnAmmoChanged, cache anything needed for runtime updates
- Implement
OnDetachedFromCharacter— clear all character-tied references
- Implement
OnWeaponFired(MuzzleLoc, FireDir)— per-shot cosmetics (material pulse, muzzle flash, sound)
- Implement
OnWeaponStoppedFiring— stop looping effects, reset material state