From 04a8761bb6c0a12fc8f07ccf3a24615c8bb8b591 Mon Sep 17 00:00:00 2001 From: Arendian <137322659+Arendian@users.noreply.github.com> Date: Mon, 15 Jan 2024 00:39:09 +0100 Subject: [PATCH] decelerator nerf and projectile penetration mini refactor (#24032) * decelerator nerf and projectile fix * datafield name difference * forgot to add component * missed datafield styling * comment made summary * CanPenetrateComponent and CollisionLayer check. * Small comment changes --- .../Projectiles/ProjectileSystem.cs | 14 +---- .../Projectiles/CanPenetrateComponent.cs | 42 ++++++++++++++ .../Projectiles/ProjectileComponent.cs | 56 ++++++++++++------- .../Projectiles/SharedProjectileSystem.cs | 48 ++++++++++++++++ .../Guns/Ammunition/Projectiles/grenade.yml | 9 ++- .../Weapons/Guns/Projectiles/projectiles.yml | 8 +-- .../Power/Generation/PA/particles.yml | 15 ++++- 7 files changed, 154 insertions(+), 38 deletions(-) create mode 100644 Content.Shared/Projectiles/CanPenetrateComponent.cs diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 8c6b84ec04..85feaf2b83 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -5,9 +5,7 @@ using Content.Shared.Camera; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.Projectiles; -using Robust.Server.GameObjects; using Robust.Shared.Physics.Events; -using Content.Shared.Mobs.Components; using Robust.Shared.Player; namespace Content.Server.Projectiles; @@ -71,17 +69,11 @@ public sealed class ProjectileSystem : SharedProjectileSystem component.DamagedEntity = true; - if (component.CanPenetrate) - { - component.DamagedEntity = false; + var afterProjectileHitEvent = new AfterProjectileHitEvent(component.Damage, target, args.OtherFixture); + RaiseLocalEvent(uid, ref afterProjectileHitEvent); - if (component.DeleteOnCollide && !HasComp(target)) - QueueDel(uid); - } - else if (component.DeleteOnCollide) - { + if (component.DeleteOnCollide) QueueDel(uid); - } if (component.ImpactEffect != null && TryComp(uid, out var xform)) { diff --git a/Content.Shared/Projectiles/CanPenetrateComponent.cs b/Content.Shared/Projectiles/CanPenetrateComponent.cs new file mode 100644 index 0000000000..6134978b1b --- /dev/null +++ b/Content.Shared/Projectiles/CanPenetrateComponent.cs @@ -0,0 +1,42 @@ +using Content.Shared.FixedPoint; +using Content.Shared.Physics; +using Robust.Shared.GameStates; + +namespace Content.Shared.Projectiles; + +[RegisterComponent, NetworkedComponent] +public sealed partial class CanPenetrateComponent : Component +{ + /// + /// Should the projectile keep the ability to deal damage after colliding. + /// + [DataField] + public bool DamageAfterCollide = true; + + /// + /// The CollisionLayer, up to and including the one set, the projectile is allowed to penetrate. + /// + /// + /// Can penetrate everything if this value is not set. + /// + [DataField] + public CollisionGroup? PenetrationLayer; + + /// + /// How many times the projectile is allowed to deal damage. + /// + /// + /// Can deal damage on every collision if this value is not set. + /// + [DataField] + public float? PenetrationPower; + + /// + /// Modifies the damage of a projectile after it has penetrated an entity. + /// + /// + /// Won't modify the projectile's damage if this value is not set. + /// + [DataField] + public FixedPoint2? DamageModifier; +} diff --git a/Content.Shared/Projectiles/ProjectileComponent.cs b/Content.Shared/Projectiles/ProjectileComponent.cs index 3b5cd33c79..c24cf2b5ee 100644 --- a/Content.Shared/Projectiles/ProjectileComponent.cs +++ b/Content.Shared/Projectiles/ProjectileComponent.cs @@ -2,57 +2,75 @@ using Content.Shared.Damage; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Projectiles; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ProjectileComponent : Component { - [ViewVariables(VVAccess.ReadWrite), DataField("impactEffect", customTypeSerializer:typeof(PrototypeIdSerializer))] - public string? ImpactEffect; + /// + /// The effect that appears when a projectile collides with an entity. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public EntProtoId? ImpactEffect; /// - /// User that shot this projectile. + /// User that shot this projectile. /// - [DataField("shooter"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? Shooter; /// - /// Weapon used to shoot. + /// Weapon used to shoot. /// - [DataField("weapon"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? Weapon; - [DataField("ignoreShooter"), AutoNetworkedField] + /// + /// The projectile spawns inside the shooter most of the time, this prevents entities from shooting themselves. + /// + [DataField, AutoNetworkedField] public bool IgnoreShooter = true; - [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] + /// + /// The amount of damage the projectile will do. + /// + [DataField(required: true)] [ViewVariables(VVAccess.ReadWrite)] public DamageSpecifier Damage = new(); - [DataField("deleteOnCollide")] + /// + /// If the projectile should be deleted on collision. + /// + [DataField] public bool DeleteOnCollide = true; - [DataField("canPenetrate")] - public bool CanPenetrate = false; - - [DataField("ignoreResistances")] + /// + /// Ignore all damage resistances the target has. + /// + [DataField] public bool IgnoreResistances = false; - // Get that juicy FPS hit sound - [DataField("soundHit")] public SoundSpecifier? SoundHit; + /// + /// Get that juicy FPS hit sound. + /// + [DataField] + public SoundSpecifier? SoundHit; - [DataField("soundForce")] + /// + /// Force the projectiles sound to play rather than potentially playing the entity's sound. + /// + [DataField] public bool ForceSound = false; /// - /// Whether this projectile will only collide with entities if it was shot from a gun (if is not null) + /// Whether this projectile will only collide with entities if it was shot from a gun (if is not null). /// - [DataField("onlyCollideWhenShot")] + [DataField] public bool OnlyCollideWhenShot = false; /// /// Whether this projectile has already damaged an entity. /// + [DataField] public bool DamagedEntity; } diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 2db497f94c..2fa5ee1c8c 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -4,12 +4,14 @@ using Content.Shared.Damage; using Content.Shared.DoAfter; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; +using Content.Shared.Mobs.Components; using Content.Shared.Throwing; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; using Robust.Shared.Serialization; @@ -32,6 +34,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(PreventCollision); + SubscribeLocalEvent(AfterProjectileHit); SubscribeLocalEvent(OnEmbedProjectileHit); SubscribeLocalEvent(OnEmbedThrowDoHit); SubscribeLocalEvent(OnEmbedActivate); @@ -160,6 +163,45 @@ public abstract partial class SharedProjectileSystem : EntitySystem { args.Cancel("pacified-cannot-throw-embed"); } + + /// + /// Checks if the projectile is allowed to penetrate the target it hit. + /// + private void AfterProjectileHit(EntityUid uid, ProjectileComponent component, ref AfterProjectileHitEvent args) + { + if (!TryComp(uid, out var damageAfterCollide)) + return; + + //Delete the projectile if it hits an entity with a CollisionLayer that has a higher value than it's PenetrationLayer. + //This allows a projectile to only penetrate a specific set of entities. + if (damageAfterCollide.PenetrationLayer != null) + { + if (args.Fixture.CollisionLayer > (int) damageAfterCollide.PenetrationLayer || + damageAfterCollide.PenetrationPower == 0) + { + QueueDel(uid); + return; + } + } + + //Allow the projectile to deal damage again. + if(damageAfterCollide.DamageAfterCollide) + component.DamagedEntity = false; + + //If the projectile has a limit on the amount of penetrations, reduce it. + if (damageAfterCollide.PenetrationPower != null) + damageAfterCollide.PenetrationPower -= 1; + + //Apply the penetration damage modifier if the projectile has one. + if (damageAfterCollide.DamageModifier != null) + component.Damage *= damageAfterCollide.DamageModifier.Value; + + //Overrides the original DeleteOnCollide if the projectile passes all penetration checks. + //This is to prevent having to set DeleteOnCollide to false on every prototype + //you want to give the ability to penetrate entities. + if(component.DeleteOnCollide) + component.DeleteOnCollide = false; + } } [Serializable, NetSerializable] @@ -186,3 +228,9 @@ public record struct ProjectileReflectAttemptEvent(EntityUid ProjUid, Projectile /// [ByRefEvent] public record struct ProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, EntityUid? Shooter = null); + +/// +/// Raised after a projectile has dealt it's damage. +/// +[ByRefEvent] +public record struct AfterProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, Fixture Fixture); diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/grenade.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/grenade.yml index 60f3e47c32..ec62121c6b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/grenade.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/grenade.yml @@ -9,10 +9,11 @@ state: buckshot - type: Projectile deleteOnCollide: false - canPenetrate: true damage: types: Blunt: 4 + - type: CanPenetrate + penetrationLayer: MobLayer - type: StaminaDamageOnCollide damage: 55 - type: TimedDespawn @@ -29,10 +30,11 @@ state: buckshot - type: Projectile deleteOnCollide: false - canPenetrate: true damage: types: Piercing: 45 + - type: CanPenetrate + penetrationLayer: MobLayer - type: TimedDespawn lifetime: 0.25 @@ -47,11 +49,12 @@ state: buckshot-flare - type: Projectile deleteOnCollide: false - canPenetrate: true damage: types: Blunt: 1 Heat: 2 + - type: CanPenetrate + penetrationLayer: MobLayer - type: IgniteOnCollide fireStacks: 3 count: 10 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 5557340724..cddbdc58cf 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -210,7 +210,7 @@ Heat: 5 soundHit: path: "/Audio/Weapons/Guns/Hits/taser_hit.ogg" - soundForce: true + forceSound: true - type: StunOnCollide stunAmount: 5 knockdownAmount: 5 @@ -256,7 +256,7 @@ Heat: 5 soundHit: path: "/Audio/Weapons/tap.ogg" - soundForce: true + forceSound: true - type: entity name : disabler bolt practice @@ -296,7 +296,7 @@ Heat: 1 soundHit: path: "/Audio/Weapons/tap.ogg" - soundForce: true + forceSound: true - type: entity name: emitter bolt @@ -865,7 +865,7 @@ Heat: 2 soundHit: path: "/Audio/Weapons/tap.ogg" - soundForce: true + forceSound: true - type: entity name: tesla gun lightning diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml index 101ebc94bd..8f89c900ca 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml @@ -13,13 +13,13 @@ map: [ "unshaded" ] - type: Projectile deleteOnCollide: false - canPenetrate: true impactEffect: null soundHit: path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg damage: types: Radiation: 25 + - type: CanPenetrate - type: Physics - type: Fixtures fixtures: @@ -64,5 +64,18 @@ state: particle0 shader: unshaded map: [ "unshaded" ] + - type: Reflective + reflective: + - Energy + - type: Projectile + deleteOnCollide: false + impactEffect: null + soundHit: + path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg + damage: + types: + Radiation: 10 + - type: TimedDespawn + lifetime: 0.6 - type: SinguloFood energy: -10