]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
decelerator nerf and projectile penetration mini refactor (#24032)
authorArendian <137322659+Arendian@users.noreply.github.com>
Sun, 14 Jan 2024 23:39:09 +0000 (00:39 +0100)
committerGitHub <noreply@github.com>
Sun, 14 Jan 2024 23:39:09 +0000 (15:39 -0800)
* 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

Content.Server/Projectiles/ProjectileSystem.cs
Content.Shared/Projectiles/CanPenetrateComponent.cs [new file with mode: 0644]
Content.Shared/Projectiles/ProjectileComponent.cs
Content.Shared/Projectiles/SharedProjectileSystem.cs
Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/grenade.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml

index 8c6b84ec047343d24c05287d82b0f8a0d6b6d8ad..85feaf2b8319b46ce2669e8566e0e38f142f94d7 100644 (file)
@@ -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<MobStateComponent>(target))
-                QueueDel(uid);
-        }
-        else if (component.DeleteOnCollide)
-        {
+        if (component.DeleteOnCollide)
             QueueDel(uid);
-        }
 
         if (component.ImpactEffect != null && TryComp<TransformComponent>(uid, out var xform))
         {
diff --git a/Content.Shared/Projectiles/CanPenetrateComponent.cs b/Content.Shared/Projectiles/CanPenetrateComponent.cs
new file mode 100644 (file)
index 0000000..6134978
--- /dev/null
@@ -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
+{
+    /// <summary>
+    ///     Should the projectile keep the ability to deal damage after colliding.
+    /// </summary>
+    [DataField]
+    public bool DamageAfterCollide = true;
+
+    /// <summary>
+    ///     The CollisionLayer, up to and including the one set, the projectile is allowed to penetrate.
+    /// </summary>
+    ///<remarks>
+    ///     Can penetrate everything if this value is not set.
+    /// </remarks>
+    [DataField]
+    public CollisionGroup? PenetrationLayer;
+
+    /// <summary>
+    ///     How many times the projectile is allowed to deal damage.
+    /// </summary>
+    /// <remarks>
+    ///     Can deal damage on every collision if this value is not set.
+    /// </remarks>
+    [DataField]
+    public float? PenetrationPower;
+
+    /// <summary>
+    ///     Modifies the damage of a projectile after it has penetrated an entity.
+    /// </summary>
+    /// <remarks>
+    ///     Won't modify the projectile's damage if this value is not set.
+    /// </remarks>
+    [DataField]
+    public FixedPoint2? DamageModifier;
+}
index 3b5cd33c791b57f4361a358d462054035602f0ea..c24cf2b5ee548c7a9062e41c343701aa178c5575 100644 (file)
@@ -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<EntityPrototype>))]
-    public string? ImpactEffect;
+    /// <summary>
+    ///     The effect that appears when a projectile collides with an entity.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public EntProtoId? ImpactEffect;
 
     /// <summary>
-    /// User that shot this projectile.
+    ///     User that shot this projectile.
     /// </summary>
-    [DataField("shooter"), AutoNetworkedField]
+    [DataField, AutoNetworkedField]
     public EntityUid? Shooter;
 
     /// <summary>
-    /// Weapon used to shoot.
+    ///     Weapon used to shoot.
     /// </summary>
-    [DataField("weapon"), AutoNetworkedField]
+    [DataField, AutoNetworkedField]
     public EntityUid? Weapon;
 
-    [DataField("ignoreShooter"), AutoNetworkedField]
+    /// <summary>
+    ///     The projectile spawns inside the shooter most of the time, this prevents entities from shooting themselves.
+    /// </summary>
+    [DataField, AutoNetworkedField]
     public bool IgnoreShooter = true;
 
-    [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)]
+    /// <summary>
+    ///     The amount of damage the projectile will do.
+    /// </summary>
+    [DataField(required: true)] [ViewVariables(VVAccess.ReadWrite)]
     public DamageSpecifier Damage = new();
 
-    [DataField("deleteOnCollide")]
+    /// <summary>
+    ///     If the projectile should be deleted on collision.
+    /// </summary>
+    [DataField]
     public bool DeleteOnCollide = true;
 
-    [DataField("canPenetrate")]
-    public bool CanPenetrate = false;
-
-    [DataField("ignoreResistances")]
+    /// <summary>
+    ///     Ignore all damage resistances the target has.
+    /// </summary>
+    [DataField]
     public bool IgnoreResistances = false;
 
-    // Get that juicy FPS hit sound
-    [DataField("soundHit")] public SoundSpecifier? SoundHit;
+    /// <summary>
+    ///     Get that juicy FPS hit sound.
+    /// </summary>
+    [DataField]
+    public SoundSpecifier? SoundHit;
 
-    [DataField("soundForce")]
+    /// <summary>
+    ///     Force the projectiles sound to play rather than potentially playing the entity's sound.
+    /// </summary>
+    [DataField]
     public bool ForceSound = false;
 
     /// <summary>
-    ///     Whether this projectile will only collide with entities if it was shot from a gun (if <see cref="Weapon"/> is not null)
+    ///     Whether this projectile will only collide with entities if it was shot from a gun (if <see cref="Weapon"/> is not null).
     /// </summary>
-    [DataField("onlyCollideWhenShot")]
+    [DataField]
     public bool OnlyCollideWhenShot = false;
 
     /// <summary>
     ///     Whether this projectile has already damaged an entity.
     /// </summary>
+    [DataField]
     public bool DamagedEntity;
 }
index 2db497f94c9edf55c7ec46e66ae46ee0c3f1af6e..2fa5ee1c8c27600920c5ff9dd3ab6dc583c72aa1 100644 (file)
@@ -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<ProjectileComponent, PreventCollideEvent>(PreventCollision);
+        SubscribeLocalEvent<ProjectileComponent, AfterProjectileHitEvent>(AfterProjectileHit);
         SubscribeLocalEvent<EmbeddableProjectileComponent, ProjectileHitEvent>(OnEmbedProjectileHit);
         SubscribeLocalEvent<EmbeddableProjectileComponent, ThrowDoHitEvent>(OnEmbedThrowDoHit);
         SubscribeLocalEvent<EmbeddableProjectileComponent, ActivateInWorldEvent>(OnEmbedActivate);
@@ -160,6 +163,45 @@ public abstract partial class SharedProjectileSystem : EntitySystem
     {
         args.Cancel("pacified-cannot-throw-embed");
     }
+
+    /// <summary>
+    /// Checks if the projectile is allowed to penetrate the target it hit.
+    /// </summary>
+    private void AfterProjectileHit(EntityUid uid, ProjectileComponent component, ref AfterProjectileHitEvent args)
+    {
+        if (!TryComp<CanPenetrateComponent>(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
 /// </summary>
 [ByRefEvent]
 public record struct ProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, EntityUid? Shooter = null);
+
+/// <summary>
+/// Raised after a projectile has dealt it's damage.
+/// </summary>
+[ByRefEvent]
+public record struct AfterProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, Fixture Fixture);
index 60f3e47c32c95501fcd00865fc751882efb99d09..ec62121c6b944e73197c7db39abdbd7e50348f8c 100644 (file)
@@ -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
     state: buckshot
   - type: Projectile
     deleteOnCollide: false
-    canPenetrate: true
     damage:
       types:
         Piercing: 45
+  - type: CanPenetrate
+    penetrationLayer: MobLayer
   - type: TimedDespawn
     lifetime: 0.25
 
     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
index 5557340724928801886157eca11f2f4e2dfd4bc0..cddbdc58cf6df8263387400d873254c17892fcf8 100644 (file)
         Heat: 5
     soundHit:
       path: "/Audio/Weapons/Guns/Hits/taser_hit.ogg"
-    soundForce: true
+    forceSound: true
   - type: StunOnCollide
     stunAmount: 5
     knockdownAmount: 5
         Heat: 5
     soundHit:
       path: "/Audio/Weapons/tap.ogg"
-    soundForce: true
+    forceSound: true
 
 - type: entity
   name : disabler bolt practice
         Heat: 1
     soundHit:
       path: "/Audio/Weapons/tap.ogg"
-    soundForce: true
+    forceSound: true
 
 - type: entity
   name: emitter bolt
         Heat: 2
     soundHit:
       path: "/Audio/Weapons/tap.ogg"
-    soundForce: true
+    forceSound: true
 
 - type: entity
   name: tesla gun lightning
index 101ebc94bd03be656acf3ca95bd6d25cc95193a3..8f89c900ca9ded0db51f94201d5c87e25b681874 100644 (file)
         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:
       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