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 USLWeaponDataAsset reference that defines fire modes, meshes, montages, and all tuning
  • StateCurrentAmmo, a replicated int32 that 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.

ConceptWhere
MaxAmmoDefined on FSLWeaponFireMode on the weapon data asset
CurrentAmmoReplicated int32 on ASLWeaponActor
DecrementAmmoDecrementEffect GE applied on authority per shot via USLGameplayAbility_Fire
IncrementVia SetCurrentAmmo — called by pickup actors or game logic
InitializationBeginPlay on authority sets CurrentAmmo = GetMaxAmmo()

#API

FunctionAuthorityPurpose
SetCurrentAmmo(int32 NewAmmo)ServerSets CurrentAmmo, clamped to [0, MaxAmmo]. Call from pickup logic or ammo grants.
GetMaxAmmo()AnyReturns WeaponData->PrimaryFireMode.MaxAmmo. Used for clamping and initialization.
OnRep_CurrentAmmo()ClientCalled automatically when CurrentAmmo replicates. Not used for HUD — the HUD binds to USLWeaponsComponent.OnAmmoChanged instead.
OnAttachedToCharacter(Character)Local clientCalled by USLWeaponsComponent after mesh attachment. Implement in BP to set up DMIs, bind delegates, cache character reference.
OnDetachedFromCharacter()Local clientCalled by USLWeaponsComponent on unequip. Clear DMI references and any other character-tied state.
OnWeaponFired(MuzzleLoc, FireDir)Local clientCalled by USLGameplayAbility_Fire each shot. Drive muzzle flash, material pulses, per-shot sounds.
OnWeaponStoppedFiring()Local clientCalled 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 WeaponData in the Details panel. CurrentAmmo initializes from MaxAmmo on BeginPlay.
  • Spawned at runtimeSpawnActor<ASLWeaponActor>, then set WeaponData before or immediately after spawning. Call SetCurrentAmmo if 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:

LayerMechanismUse case
GlobalBool on SLGameModeBase.bInfiniteAmmoGame mode (e.g. dev mode, infinite ammo playlist)
Per-characterSLTags.States.Weapon.InfiniteAmmo tag on the ASC, applied via duration-based GEPowerup pickup — expires automatically when the GE duration ends
Per fire modebInfiniteAmmo bool on FSLWeaponFireModeWeapons 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 WeaponData as 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 to OnAmmoChanged, 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