diff --git a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs index 3fd46f6efb..cf9c15a10d 100644 --- a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs +++ b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Robust.Shared.Audio; using Robust.Shared.GameStates; namespace Content.Shared.Projectiles; @@ -33,4 +34,10 @@ public sealed partial class EmbeddableProjectileComponent : Component /// [ViewVariables(VVAccess.ReadWrite), DataField("offset"), AutoNetworkedField] public Vector2 Offset = Vector2.Zero; + + /// + /// Sound to play after embedding into a hit target. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("sound"), AutoNetworkedField] + public SoundSpecifier? Sound; } diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 541a2dfbee..9365956601 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Projectiles; using Content.Shared.Sound.Components; using Content.Shared.Throwing; using Content.Shared.Weapons.Ranged.Components; +using Robust.Shared.Audio; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Physics; @@ -13,145 +14,151 @@ using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; using Robust.Shared.Serialization; -namespace Content.Shared.Projectiles +namespace Content.Shared.Projectiles; + +public abstract class SharedProjectileSystem : EntitySystem { - public abstract class SharedProjectileSystem : EntitySystem + public const string ProjectileFixture = "projectile"; + + [Dependency] private readonly INetManager _netManager = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + public override void Initialize() { - public const string ProjectileFixture = "projectile"; + base.Initialize(); - [Dependency] private readonly INetManager _netManager = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; + SubscribeLocalEvent(PreventCollision); + SubscribeLocalEvent(OnEmbedProjectileHit); + SubscribeLocalEvent(OnEmbedThrowDoHit); + SubscribeLocalEvent(OnEmbedActivate); + SubscribeLocalEvent(OnEmbedRemove); + } - public override void Initialize() + private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent component, ActivateInWorldEvent args) + { + // Nuh uh + if (component.RemovalTime == null) + return; + + if (args.Handled || !TryComp(uid, out var physics) || physics.BodyType != BodyType.Static) + return; + + args.Handled = true; + + _doAfter.TryStartDoAfter(new DoAfterArgs(args.User, component.RemovalTime.Value, + new RemoveEmbeddedProjectileEvent(), eventTarget: uid, target: uid) { - base.Initialize(); - SubscribeLocalEvent(PreventCollision); - SubscribeLocalEvent(OnEmbedProjectileHit); - SubscribeLocalEvent(OnEmbedThrowDoHit); - SubscribeLocalEvent(OnEmbedActivate); - SubscribeLocalEvent(OnEmbedRemove); + DistanceThreshold = SharedInteractionSystem.InteractionRange, + }); + } + + private void OnEmbedRemove(EntityUid uid, EmbeddableProjectileComponent component, RemoveEmbeddedProjectileEvent args) + { + // Whacky prediction issues. + if (args.Cancelled || _netManager.IsClient) + return; + + if (component.DeleteOnRemove) + { + QueueDel(uid); + return; } - private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent component, ActivateInWorldEvent args) + var xform = Transform(uid); + TryComp(uid, out var physics); + _physics.SetBodyType(uid, BodyType.Dynamic, body: physics, xform: xform); + _transform.AttachToGridOrMap(uid, xform); + + // Land it just coz uhhh yeah + var landEv = new LandEvent(args.User, true); + RaiseLocalEvent(uid, ref landEv); + _physics.WakeBody(uid, body: physics); + } + + private void OnEmbedThrowDoHit(EntityUid uid, EmbeddableProjectileComponent component, ThrowDoHitEvent args) + { + Embed(uid, args.Target, component); + } + + private void OnEmbedProjectileHit(EntityUid uid, EmbeddableProjectileComponent component, ref ProjectileHitEvent args) + { + Embed(uid, args.Target, component); + + // Raise a specific event for projectiles. + if (TryComp(uid, out var projectile)) { - // Nuh uh - if (component.RemovalTime == null) - return; + var ev = new ProjectileEmbedEvent(projectile.Shooter, projectile.Weapon, args.Target); + RaiseLocalEvent(uid, ref ev); + } + } - if (args.Handled || !TryComp(uid, out var physics) || physics.BodyType != BodyType.Static) - return; + private void Embed(EntityUid uid, EntityUid target, EmbeddableProjectileComponent component) + { + TryComp(uid, out var physics); + _physics.SetLinearVelocity(uid, Vector2.Zero, body: physics); + _physics.SetBodyType(uid, BodyType.Static, body: physics); + var xform = Transform(uid); + _transform.SetParent(uid, xform, target); - args.Handled = true; - - _doAfter.TryStartDoAfter(new DoAfterArgs(args.User, component.RemovalTime.Value, - new RemoveEmbeddedProjectileEvent(), eventTarget: uid, target: uid) - { - DistanceThreshold = SharedInteractionSystem.InteractionRange, - }); + if (component.Offset != Vector2.Zero) + { + _transform.SetLocalPosition(xform, xform.LocalPosition + xform.LocalRotation.RotateVec(component.Offset)); } - private void OnEmbedRemove(EntityUid uid, EmbeddableProjectileComponent component, RemoveEmbeddedProjectileEvent args) + if (component.Sound != null) { - // Whacky prediction issues. - if (args.Cancelled || _netManager.IsClient) - return; - - if (component.DeleteOnRemove) - { - QueueDel(uid); - return; - } - - var xform = Transform(uid); - TryComp(uid, out var physics); - _physics.SetBodyType(uid, BodyType.Dynamic, body: physics, xform: xform); - _transform.AttachToGridOrMap(uid, xform); - - // Land it just coz uhhh yeah - var landEv = new LandEvent(args.User, true); - RaiseLocalEvent(uid, ref landEv); - _physics.WakeBody(uid, body: physics); + _audio.PlayPredicted(component.Sound, uid, null); } + } - private void OnEmbedThrowDoHit(EntityUid uid, EmbeddableProjectileComponent component, ThrowDoHitEvent args) + private void PreventCollision(EntityUid uid, ProjectileComponent component, ref PreventCollideEvent args) + { + if (component.IgnoreShooter && args.OtherEntity == component.Shooter) { - Embed(uid, args.Target, component); + args.Cancelled = true; } + } - private void OnEmbedProjectileHit(EntityUid uid, EmbeddableProjectileComponent component, ref ProjectileHitEvent args) - { - Embed(uid, args.Target, component); + public void SetShooter(ProjectileComponent component, EntityUid uid) + { + if (component.Shooter == uid) + return; - // Raise a specific event for projectiles. - if (TryComp(uid, out var projectile)) - { - var ev = new ProjectileEmbedEvent(projectile.Shooter, projectile.Weapon, args.Target); - RaiseLocalEvent(uid, ref ev); - } - } - - private void Embed(EntityUid uid, EntityUid target, EmbeddableProjectileComponent component) - { - TryComp(uid, out var physics); - _physics.SetLinearVelocity(uid, Vector2.Zero, body: physics); - _physics.SetBodyType(uid, BodyType.Static, body: physics); - var xform = Transform(uid); - _transform.SetParent(uid, xform, target); - - if (component.Offset != Vector2.Zero) - { - _transform.SetLocalPosition(xform, xform.LocalPosition + xform.LocalRotation.RotateVec(component.Offset)); - } - } - - private void PreventCollision(EntityUid uid, ProjectileComponent component, ref PreventCollideEvent args) - { - if (component.IgnoreShooter && args.OtherEntity == component.Shooter) - { - args.Cancelled = true; - } - } - - public void SetShooter(ProjectileComponent component, EntityUid uid) - { - if (component.Shooter == uid) - return; - - component.Shooter = uid; - Dirty(uid, component); - } - - [Serializable, NetSerializable] - private sealed class RemoveEmbeddedProjectileEvent : DoAfterEvent - { - public override DoAfterEvent Clone() => this; - } + component.Shooter = uid; + Dirty(uid, component); } [Serializable, NetSerializable] - public sealed class ImpactEffectEvent : EntityEventArgs + private sealed class RemoveEmbeddedProjectileEvent : DoAfterEvent { - public string Prototype; - public EntityCoordinates Coordinates; - - public ImpactEffectEvent(string prototype, EntityCoordinates coordinates) - { - Prototype = prototype; - Coordinates = coordinates; - } + public override DoAfterEvent Clone() => this; } - - /// - /// Raised when entity is just about to be hit with projectile but can reflect it - /// - [ByRefEvent] - public record struct ProjectileReflectAttemptEvent(EntityUid ProjUid, ProjectileComponent Component, bool Cancelled); - - /// - /// Raised when projectile hits other entity - /// - [ByRefEvent] - public readonly record struct ProjectileHitEvent(EntityUid Target); } + +[Serializable, NetSerializable] +public sealed class ImpactEffectEvent : EntityEventArgs +{ + public string Prototype; + public EntityCoordinates Coordinates; + + public ImpactEffectEvent(string prototype, EntityCoordinates coordinates) + { + Prototype = prototype; + Coordinates = coordinates; + } +} + +/// +/// Raised when entity is just about to be hit with projectile but can reflect it +/// +[ByRefEvent] +public record struct ProjectileReflectAttemptEvent(EntityUid ProjUid, ProjectileComponent Component, bool Cancelled); + +/// +/// Raised when projectile hits other entity +/// +[ByRefEvent] +public readonly record struct ProjectileHitEvent(EntityUid Target); diff --git a/Resources/Audio/Weapons/attributions.yml b/Resources/Audio/Weapons/attributions.yml index 5eaba3cacb..6e59d34d59 100644 --- a/Resources/Audio/Weapons/attributions.yml +++ b/Resources/Audio/Weapons/attributions.yml @@ -19,3 +19,12 @@ copyright: "User Mystovski on freesound.org. Modified by LankLTE on github." source: "https://freesound.org/people/Mystovski/sounds/201111/" +- files: ["genhit3.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Taken from tgstation." + source: "https://github.com/tgstation/tgstation/blob/a7f525bce9a359ab5282fc754078cd4b5678a006/sound/weapons/genhit3.ogg" + +- files: ["star_hit.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Based on genhit3.ogg from tgstation, modified by deltanedas on github." + source: "https://github.com/deltanedas" diff --git a/Resources/Audio/Weapons/star_hit.ogg b/Resources/Audio/Weapons/star_hit.ogg new file mode 100644 index 0000000000..5de4051e8b Binary files /dev/null and b/Resources/Audio/Weapons/star_hit.ogg differ diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/throwing_stars.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/throwing_stars.yml new file mode 100644 index 0000000000..198f634c16 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/throwing_stars.yml @@ -0,0 +1,26 @@ +- type: entity + parent: BaseItem + id: ThrowingStar + name: throwing star + description: An ancient weapon still used to this day, due to its ease of lodging itself into its victim's body parts. + components: + - type: Sprite + sprite: Objects/Weapons/Throwable/throwing_star.rsi + state: icon + - type: Fixtures + fixtures: + fix1: + shape: !type:PhysShapeCircle + radius: 0.2 + density: 5 + mask: + - ItemMask + restitution: 0.3 + friction: 0.2 + - type: EmbeddableProjectile + sound: /Audio/Weapons/star_hit.ogg + - type: DamageOtherOnHit + damage: + types: + Slash: 8 + Piercing: 10 diff --git a/Resources/Textures/Objects/Weapons/Throwable/throwing_star.rsi/icon.png b/Resources/Textures/Objects/Weapons/Throwable/throwing_star.rsi/icon.png new file mode 100644 index 0000000000..239b0304ab Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Throwable/throwing_star.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Weapons/Throwable/throwing_star.rsi/meta.json b/Resources/Textures/Objects/Weapons/Throwable/throwing_star.rsi/meta.json new file mode 100644 index 0000000000..44d65d9d5e --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Throwable/throwing_star.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC0-1.0", + "copyright": "Created for SS14 by deltanedas (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + } + ] +}