Fix effects (#27533)
* Fix effects - Fix muzzle flash rotations. - Fix effects so they update every frame. - Fix effects tanking client performance. * Fix merge artifact
This commit is contained in:
17
Content.Client/Animations/TrackUserComponent.cs
Normal file
17
Content.Client/Animations/TrackUserComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Content.Client.Animations;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entities with this component tracks the user's world position every frame.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class TrackUserComponent : Component
|
||||||
|
{
|
||||||
|
public EntityUid? User;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Offset in the direction of the entity's rotation.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 Offset = Vector2.Zero;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Content.Client.Animations;
|
||||||
using Content.Client.Weapons.Melee.Components;
|
using Content.Client.Weapons.Melee.Components;
|
||||||
using Content.Shared.Weapons.Melee;
|
using Content.Shared.Weapons.Melee;
|
||||||
using Robust.Client.Animations;
|
using Robust.Client.Animations;
|
||||||
@@ -59,17 +60,20 @@ public sealed partial class MeleeWeaponSystem
|
|||||||
var distance = Math.Clamp(localPos.Length() / 2f, 0.2f, 1f);
|
var distance = Math.Clamp(localPos.Length() / 2f, 0.2f, 1f);
|
||||||
|
|
||||||
var xform = _xformQuery.GetComponent(animationUid);
|
var xform = _xformQuery.GetComponent(animationUid);
|
||||||
|
TrackUserComponent track;
|
||||||
|
|
||||||
switch (arcComponent.Animation)
|
switch (arcComponent.Animation)
|
||||||
{
|
{
|
||||||
case WeaponArcAnimation.Slash:
|
case WeaponArcAnimation.Slash:
|
||||||
arcComponent.User = user;
|
track = EnsureComp<TrackUserComponent>(animationUid);
|
||||||
|
track.User = user;
|
||||||
_animation.Play(animationUid, GetSlashAnimation(sprite, angle, spriteRotation), SlashAnimationKey);
|
_animation.Play(animationUid, GetSlashAnimation(sprite, angle, spriteRotation), SlashAnimationKey);
|
||||||
if (arcComponent.Fadeout)
|
if (arcComponent.Fadeout)
|
||||||
_animation.Play(animationUid, GetFadeAnimation(sprite, 0.065f, 0.065f + 0.05f), FadeAnimationKey);
|
_animation.Play(animationUid, GetFadeAnimation(sprite, 0.065f, 0.065f + 0.05f), FadeAnimationKey);
|
||||||
break;
|
break;
|
||||||
case WeaponArcAnimation.Thrust:
|
case WeaponArcAnimation.Thrust:
|
||||||
arcComponent.User = user;
|
track = EnsureComp<TrackUserComponent>(animationUid);
|
||||||
|
track.User = user;
|
||||||
_animation.Play(animationUid, GetThrustAnimation(sprite, distance, spriteRotation), ThrustAnimationKey);
|
_animation.Play(animationUid, GetThrustAnimation(sprite, distance, spriteRotation), ThrustAnimationKey);
|
||||||
if (arcComponent.Fadeout)
|
if (arcComponent.Fadeout)
|
||||||
_animation.Play(animationUid, GetFadeAnimation(sprite, 0.05f, 0.15f), FadeAnimationKey);
|
_animation.Play(animationUid, GetFadeAnimation(sprite, 0.05f, 0.15f), FadeAnimationKey);
|
||||||
@@ -206,15 +210,23 @@ public sealed partial class MeleeWeaponSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the effect positions to follow the user
|
/// Updates the effect positions to follow the user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void UpdateEffects(float frameTime)
|
private void UpdateEffects()
|
||||||
{
|
{
|
||||||
var arcQuery = EntityQueryEnumerator<TransformComponent, WeaponArcVisualsComponent>();
|
var query = EntityQueryEnumerator<TrackUserComponent, TransformComponent>();
|
||||||
while(arcQuery.MoveNext(out var uid, out var xform, out var arcComponent))
|
while (query.MoveNext(out var arcComponent, out var xform))
|
||||||
{
|
{
|
||||||
if (arcComponent.User == null)
|
if (arcComponent.User == null)
|
||||||
continue;
|
continue;
|
||||||
var userPos = TransformSystem.GetWorldPosition(arcComponent.User.Value);
|
|
||||||
TransformSystem.SetWorldPosition(xform, userPos);
|
Vector2 targetPos = TransformSystem.GetWorldPosition(arcComponent.User.Value);
|
||||||
|
|
||||||
|
if (arcComponent.Offset != Vector2.Zero)
|
||||||
|
{
|
||||||
|
var entRotation = TransformSystem.GetWorldRotation(xform);
|
||||||
|
targetPos += entRotation.RotateVec(arcComponent.Offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformSystem.SetWorldPosition(xform, targetPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,10 +43,15 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||||||
UpdatesOutsidePrediction = true;
|
UpdatesOutsidePrediction = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void FrameUpdate(float frameTime)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(frameTime);
|
||||||
|
UpdateEffects();
|
||||||
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
UpdateEffects(frameTime);
|
|
||||||
|
|
||||||
if (!Timing.IsFirstTimePredicted)
|
if (!Timing.IsFirstTimePredicted)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Content.Client.Animations;
|
||||||
using Content.Client.Items;
|
using Content.Client.Items;
|
||||||
using Content.Client.Weapons.Ranged.Components;
|
using Content.Client.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Camera;
|
using Content.Shared.Camera;
|
||||||
@@ -15,6 +16,7 @@ using Robust.Client.Player;
|
|||||||
using Robust.Shared.Animations;
|
using Robust.Shared.Animations;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem;
|
using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem;
|
||||||
@@ -24,13 +26,14 @@ namespace Content.Client.Weapons.Ranged.Systems;
|
|||||||
|
|
||||||
public sealed partial class GunSystem : SharedGunSystem
|
public sealed partial class GunSystem : SharedGunSystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||||
[Dependency] private readonly IPlayerManager _player = default!;
|
[Dependency] private readonly IPlayerManager _player = default!;
|
||||||
[Dependency] private readonly AnimationPlayerSystem _animPlayer = default!;
|
[Dependency] private readonly AnimationPlayerSystem _animPlayer = default!;
|
||||||
[Dependency] private readonly InputSystem _inputSystem = default!;
|
[Dependency] private readonly InputSystem _inputSystem = default!;
|
||||||
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
|
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
|
||||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
[Dependency] private readonly SharedMapSystem _maps = default!;
|
||||||
|
|
||||||
[ValidatePrototypeId<EntityPrototype>]
|
[ValidatePrototypeId<EntityPrototype>]
|
||||||
public const string HitscanProto = "HitscanEffect";
|
public const string HitscanProto = "HitscanEffect";
|
||||||
@@ -123,7 +126,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_animPlayer.Play(ent, null, anim, "hitscan-effect");
|
_animPlayer.Play(ent, anim, "hitscan-effect");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,6 +192,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
// to just delete the spawned entities. This is for programmer sanity despite the wasted perf.
|
// to just delete the spawned entities. This is for programmer sanity despite the wasted perf.
|
||||||
// This also means any ammo specific stuff can be grabbed as necessary.
|
// This also means any ammo specific stuff can be grabbed as necessary.
|
||||||
var direction = fromCoordinates.ToMapPos(EntityManager, TransformSystem) - toCoordinates.ToMapPos(EntityManager, TransformSystem);
|
var direction = fromCoordinates.ToMapPos(EntityManager, TransformSystem) - toCoordinates.ToMapPos(EntityManager, TransformSystem);
|
||||||
|
var worldAngle = direction.ToAngle().Opposite();
|
||||||
|
|
||||||
foreach (var (ent, shootable) in ammo)
|
foreach (var (ent, shootable) in ammo)
|
||||||
{
|
{
|
||||||
@@ -208,7 +212,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
if (!cartridge.Spent)
|
if (!cartridge.Spent)
|
||||||
{
|
{
|
||||||
SetCartridgeSpent(ent!.Value, cartridge, true);
|
SetCartridgeSpent(ent!.Value, cartridge, true);
|
||||||
MuzzleFlash(gunUid, cartridge, user);
|
MuzzleFlash(gunUid, cartridge, worldAngle, user);
|
||||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||||
Recoil(user, direction, gun.CameraRecoilScalarModified);
|
Recoil(user, direction, gun.CameraRecoilScalarModified);
|
||||||
// TODO: Can't predict entity deletions.
|
// TODO: Can't predict entity deletions.
|
||||||
@@ -226,7 +230,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case AmmoComponent newAmmo:
|
case AmmoComponent newAmmo:
|
||||||
MuzzleFlash(gunUid, newAmmo, user);
|
MuzzleFlash(gunUid, newAmmo, worldAngle, user);
|
||||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||||
Recoil(user, direction, gun.CameraRecoilScalarModified);
|
Recoil(user, direction, gun.CameraRecoilScalarModified);
|
||||||
if (IsClientSide(ent!.Value))
|
if (IsClientSide(ent!.Value))
|
||||||
@@ -258,33 +262,41 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
PopupSystem.PopupEntity(message, uid.Value, user.Value);
|
PopupSystem.PopupEntity(message, uid.Value, user.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CreateEffect(EntityUid uid, MuzzleFlashEvent message, EntityUid? user = null)
|
protected override void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null)
|
||||||
{
|
{
|
||||||
if (!Timing.IsFirstTimePredicted)
|
if (!Timing.IsFirstTimePredicted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var gunXform = Transform(gunUid);
|
||||||
|
var gridUid = gunXform.GridUid;
|
||||||
EntityCoordinates coordinates;
|
EntityCoordinates coordinates;
|
||||||
|
|
||||||
if (message.MatchRotation)
|
if (TryComp(gridUid, out MapGridComponent? mapGrid))
|
||||||
coordinates = new EntityCoordinates(uid, Vector2.Zero);
|
{
|
||||||
else if (TryComp<TransformComponent>(uid, out var xform))
|
coordinates = new EntityCoordinates(gridUid.Value, _maps.LocalToGrid(gridUid.Value, mapGrid, gunXform.Coordinates));
|
||||||
coordinates = xform.Coordinates;
|
}
|
||||||
|
else if (gunXform.MapUid != null)
|
||||||
|
{
|
||||||
|
coordinates = new EntityCoordinates(gunXform.MapUid.Value, TransformSystem.GetWorldPosition(gunXform));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (!coordinates.IsValid(EntityManager))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var ent = Spawn(message.Prototype, coordinates);
|
var ent = Spawn(message.Prototype, coordinates);
|
||||||
|
TransformSystem.SetWorldRotationNoLerp(ent, message.Angle);
|
||||||
|
|
||||||
var effectXform = Transform(ent);
|
if (user != null)
|
||||||
TransformSystem.SetLocalPositionRotation(effectXform,
|
{
|
||||||
effectXform.LocalPosition + new Vector2(0f, -0.5f),
|
var track = EnsureComp<TrackUserComponent>(ent);
|
||||||
effectXform.LocalRotation - MathF.PI / 2);
|
track.User = user;
|
||||||
|
track.Offset = Vector2.UnitX / 2f;
|
||||||
|
}
|
||||||
|
|
||||||
var lifetime = 0.4f;
|
var lifetime = 0.4f;
|
||||||
|
|
||||||
if (TryComp<TimedDespawnComponent>(uid, out var despawn))
|
if (TryComp<TimedDespawnComponent>(gunUid, out var despawn))
|
||||||
{
|
{
|
||||||
lifetime = despawn.Lifetime;
|
lifetime = despawn.Lifetime;
|
||||||
}
|
}
|
||||||
@@ -309,18 +321,17 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
};
|
};
|
||||||
|
|
||||||
_animPlayer.Play(ent, anim, "muzzle-flash");
|
_animPlayer.Play(ent, anim, "muzzle-flash");
|
||||||
if (!TryComp(uid, out PointLightComponent? light))
|
if (!TryComp(gunUid, out PointLightComponent? light))
|
||||||
{
|
{
|
||||||
light = (PointLightComponent) _factory.GetComponent(typeof(PointLightComponent));
|
light = (PointLightComponent) _factory.GetComponent(typeof(PointLightComponent));
|
||||||
light.Owner = uid;
|
|
||||||
light.NetSyncEnabled = false;
|
light.NetSyncEnabled = false;
|
||||||
AddComp(uid, light);
|
AddComp(gunUid, light);
|
||||||
}
|
}
|
||||||
|
|
||||||
Lights.SetEnabled(uid, true, light);
|
Lights.SetEnabled(gunUid, true, light);
|
||||||
Lights.SetRadius(uid, 2f, light);
|
Lights.SetRadius(gunUid, 2f, light);
|
||||||
Lights.SetColor(uid, Color.FromHex("#cc8e2b"), light);
|
Lights.SetColor(gunUid, Color.FromHex("#cc8e2b"), light);
|
||||||
Lights.SetEnergy(uid, 5f, light);
|
Lights.SetEnergy(gunUid, 5f, light);
|
||||||
|
|
||||||
var animTwo = new Animation()
|
var animTwo = new Animation()
|
||||||
{
|
{
|
||||||
@@ -352,9 +363,9 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var uidPlayer = EnsureComp<AnimationPlayerComponent>(uid);
|
var uidPlayer = EnsureComp<AnimationPlayerComponent>(gunUid);
|
||||||
|
|
||||||
_animPlayer.Stop(uid, uidPlayer, "muzzle-flash-light");
|
_animPlayer.Stop(gunUid, uidPlayer, "muzzle-flash-light");
|
||||||
_animPlayer.Play(uid, uidPlayer, animTwo,"muzzle-flash-light");
|
_animPlayer.Play((gunUid, uidPlayer), animTwo,"muzzle-flash-light");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
});
|
});
|
||||||
|
|
||||||
SetCartridgeSpent(ent.Value, cartridge, true);
|
SetCartridgeSpent(ent.Value, cartridge, true);
|
||||||
MuzzleFlash(gunUid, cartridge, user);
|
MuzzleFlash(gunUid, cartridge, mapDirection.ToAngle(), user);
|
||||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||||
|
|
||||||
if (cartridge.DeleteOnSpawn)
|
if (cartridge.DeleteOnSpawn)
|
||||||
@@ -175,7 +175,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
// Ammo shoots itself
|
// Ammo shoots itself
|
||||||
case AmmoComponent newAmmo:
|
case AmmoComponent newAmmo:
|
||||||
shotProjectiles.Add(ent!.Value);
|
shotProjectiles.Add(ent!.Value);
|
||||||
MuzzleFlash(gunUid, newAmmo, user);
|
MuzzleFlash(gunUid, newAmmo, mapDirection.ToAngle(), user);
|
||||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||||
ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, gunUid, user);
|
ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, gunUid, user);
|
||||||
break;
|
break;
|
||||||
@@ -326,9 +326,9 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
|
|
||||||
protected override void Popup(string message, EntityUid? uid, EntityUid? user) { }
|
protected override void Popup(string message, EntityUid? uid, EntityUid? user) { }
|
||||||
|
|
||||||
protected override void CreateEffect(EntityUid uid, MuzzleFlashEvent message, EntityUid? user = null)
|
protected override void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null)
|
||||||
{
|
{
|
||||||
var filter = Filter.Pvs(uid, entityManager: EntityManager);
|
var filter = Filter.Pvs(gunUid, entityManager: EntityManager);
|
||||||
|
|
||||||
if (TryComp<ActorComponent>(user, out var actor))
|
if (TryComp<ActorComponent>(user, out var actor))
|
||||||
filter.RemovePlayer(actor.PlayerSession);
|
filter.RemovePlayer(actor.PlayerSession);
|
||||||
|
|||||||
@@ -11,15 +11,12 @@ public sealed class MuzzleFlashEvent : EntityEventArgs
|
|||||||
public NetEntity Uid;
|
public NetEntity Uid;
|
||||||
public string Prototype;
|
public string Prototype;
|
||||||
|
|
||||||
/// <summary>
|
public Angle Angle;
|
||||||
/// Should the effect match the rotation of the entity.
|
|
||||||
/// </summary>
|
|
||||||
public bool MatchRotation;
|
|
||||||
|
|
||||||
public MuzzleFlashEvent(NetEntity uid, string prototype, bool matchRotation = false)
|
public MuzzleFlashEvent(NetEntity uid, string prototype, Angle angle)
|
||||||
{
|
{
|
||||||
Uid = uid;
|
Uid = uid;
|
||||||
Prototype = prototype;
|
Prototype = prototype;
|
||||||
MatchRotation = matchRotation;
|
Angle = angle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,7 +136,6 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
gun.ShootCoordinates = GetCoordinates(msg.Coordinates);
|
gun.ShootCoordinates = GetCoordinates(msg.Coordinates);
|
||||||
Log.Debug($"Set shoot coordinates to {gun.ShootCoordinates}");
|
|
||||||
AttemptShoot(user.Value, ent, gun);
|
AttemptShoot(user.Value, ent, gun);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +194,6 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
if (gun.ShotCounter == 0)
|
if (gun.ShotCounter == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Log.Debug($"Stopped shooting {ToPrettyString(uid)}");
|
|
||||||
gun.ShotCounter = 0;
|
gun.ShotCounter = 0;
|
||||||
gun.ShootCoordinates = null;
|
gun.ShootCoordinates = null;
|
||||||
Dirty(uid, gun);
|
Dirty(uid, gun);
|
||||||
@@ -461,7 +459,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
RemCompDeferred<AmmoComponent>(uid);
|
RemCompDeferred<AmmoComponent>(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void MuzzleFlash(EntityUid gun, AmmoComponent component, EntityUid? user = null)
|
protected void MuzzleFlash(EntityUid gun, AmmoComponent component, Angle worldAngle, EntityUid? user = null)
|
||||||
{
|
{
|
||||||
var attemptEv = new GunMuzzleFlashAttemptEvent();
|
var attemptEv = new GunMuzzleFlashAttemptEvent();
|
||||||
RaiseLocalEvent(gun, ref attemptEv);
|
RaiseLocalEvent(gun, ref attemptEv);
|
||||||
@@ -473,7 +471,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
if (sprite == null)
|
if (sprite == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ev = new MuzzleFlashEvent(GetNetEntity(gun), sprite, user == gun);
|
var ev = new MuzzleFlashEvent(GetNetEntity(gun), sprite, worldAngle);
|
||||||
CreateEffect(gun, ev, user);
|
CreateEffect(gun, ev, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +520,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
Dirty(gun);
|
Dirty(gun);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void CreateEffect(EntityUid uid, MuzzleFlashEvent message, EntityUid? user = null);
|
protected abstract void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for animated effects on the client.
|
/// Used for animated effects on the client.
|
||||||
|
|||||||
Reference in New Issue
Block a user