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
This commit is contained in:
Arendian
2024-01-15 00:39:09 +01:00
committed by GitHub
parent f9a57dc11e
commit 04a8761bb6
7 changed files with 154 additions and 38 deletions

View File

@@ -5,9 +5,7 @@ using Content.Shared.Camera;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Projectiles; using Content.Shared.Projectiles;
using Robust.Server.GameObjects;
using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Events;
using Content.Shared.Mobs.Components;
using Robust.Shared.Player; using Robust.Shared.Player;
namespace Content.Server.Projectiles; namespace Content.Server.Projectiles;
@@ -71,17 +69,11 @@ public sealed class ProjectileSystem : SharedProjectileSystem
component.DamagedEntity = true; component.DamagedEntity = true;
if (component.CanPenetrate) var afterProjectileHitEvent = new AfterProjectileHitEvent(component.Damage, target, args.OtherFixture);
{ RaiseLocalEvent(uid, ref afterProjectileHitEvent);
component.DamagedEntity = false;
if (component.DeleteOnCollide && !HasComp<MobStateComponent>(target)) if (component.DeleteOnCollide)
QueueDel(uid);
}
else if (component.DeleteOnCollide)
{
QueueDel(uid); QueueDel(uid);
}
if (component.ImpactEffect != null && TryComp<TransformComponent>(uid, out var xform)) if (component.ImpactEffect != null && TryComp<TransformComponent>(uid, out var xform))
{ {

View File

@@ -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;
}

View File

@@ -2,57 +2,75 @@ using Content.Shared.Damage;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Projectiles; namespace Content.Shared.Projectiles;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ProjectileComponent : Component public sealed partial class ProjectileComponent : Component
{ {
[ViewVariables(VVAccess.ReadWrite), DataField("impactEffect", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))] /// <summary>
public string? ImpactEffect; /// The effect that appears when a projectile collides with an entity.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public EntProtoId? ImpactEffect;
/// <summary> /// <summary>
/// User that shot this projectile. /// User that shot this projectile.
/// </summary> /// </summary>
[DataField("shooter"), AutoNetworkedField] [DataField, AutoNetworkedField]
public EntityUid? Shooter; public EntityUid? Shooter;
/// <summary> /// <summary>
/// Weapon used to shoot. /// Weapon used to shoot.
/// </summary> /// </summary>
[DataField("weapon"), AutoNetworkedField] [DataField, AutoNetworkedField]
public EntityUid? Weapon; 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; 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(); public DamageSpecifier Damage = new();
[DataField("deleteOnCollide")] /// <summary>
/// If the projectile should be deleted on collision.
/// </summary>
[DataField]
public bool DeleteOnCollide = true; public bool DeleteOnCollide = true;
[DataField("canPenetrate")] /// <summary>
public bool CanPenetrate = false; /// Ignore all damage resistances the target has.
/// </summary>
[DataField("ignoreResistances")] [DataField]
public bool IgnoreResistances = false; public bool IgnoreResistances = false;
// Get that juicy FPS hit sound /// <summary>
[DataField("soundHit")] public SoundSpecifier? SoundHit; /// 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; public bool ForceSound = false;
/// <summary> /// <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> /// </summary>
[DataField("onlyCollideWhenShot")] [DataField]
public bool OnlyCollideWhenShot = false; public bool OnlyCollideWhenShot = false;
/// <summary> /// <summary>
/// Whether this projectile has already damaged an entity. /// Whether this projectile has already damaged an entity.
/// </summary> /// </summary>
[DataField]
public bool DamagedEntity; public bool DamagedEntity;
} }

View File

@@ -4,12 +4,14 @@ using Content.Shared.Damage;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Mobs.Components;
using Content.Shared.Throwing; using Content.Shared.Throwing;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -32,6 +34,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ProjectileComponent, PreventCollideEvent>(PreventCollision); SubscribeLocalEvent<ProjectileComponent, PreventCollideEvent>(PreventCollision);
SubscribeLocalEvent<ProjectileComponent, AfterProjectileHitEvent>(AfterProjectileHit);
SubscribeLocalEvent<EmbeddableProjectileComponent, ProjectileHitEvent>(OnEmbedProjectileHit); SubscribeLocalEvent<EmbeddableProjectileComponent, ProjectileHitEvent>(OnEmbedProjectileHit);
SubscribeLocalEvent<EmbeddableProjectileComponent, ThrowDoHitEvent>(OnEmbedThrowDoHit); SubscribeLocalEvent<EmbeddableProjectileComponent, ThrowDoHitEvent>(OnEmbedThrowDoHit);
SubscribeLocalEvent<EmbeddableProjectileComponent, ActivateInWorldEvent>(OnEmbedActivate); SubscribeLocalEvent<EmbeddableProjectileComponent, ActivateInWorldEvent>(OnEmbedActivate);
@@ -160,6 +163,45 @@ public abstract partial class SharedProjectileSystem : EntitySystem
{ {
args.Cancel("pacified-cannot-throw-embed"); 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] [Serializable, NetSerializable]
@@ -186,3 +228,9 @@ public record struct ProjectileReflectAttemptEvent(EntityUid ProjUid, Projectile
/// </summary> /// </summary>
[ByRefEvent] [ByRefEvent]
public record struct ProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, EntityUid? Shooter = null); 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);

View File

@@ -9,10 +9,11 @@
state: buckshot state: buckshot
- type: Projectile - type: Projectile
deleteOnCollide: false deleteOnCollide: false
canPenetrate: true
damage: damage:
types: types:
Blunt: 4 Blunt: 4
- type: CanPenetrate
penetrationLayer: MobLayer
- type: StaminaDamageOnCollide - type: StaminaDamageOnCollide
damage: 55 damage: 55
- type: TimedDespawn - type: TimedDespawn
@@ -29,10 +30,11 @@
state: buckshot state: buckshot
- type: Projectile - type: Projectile
deleteOnCollide: false deleteOnCollide: false
canPenetrate: true
damage: damage:
types: types:
Piercing: 45 Piercing: 45
- type: CanPenetrate
penetrationLayer: MobLayer
- type: TimedDespawn - type: TimedDespawn
lifetime: 0.25 lifetime: 0.25
@@ -47,11 +49,12 @@
state: buckshot-flare state: buckshot-flare
- type: Projectile - type: Projectile
deleteOnCollide: false deleteOnCollide: false
canPenetrate: true
damage: damage:
types: types:
Blunt: 1 Blunt: 1
Heat: 2 Heat: 2
- type: CanPenetrate
penetrationLayer: MobLayer
- type: IgniteOnCollide - type: IgniteOnCollide
fireStacks: 3 fireStacks: 3
count: 10 count: 10

View File

@@ -210,7 +210,7 @@
Heat: 5 Heat: 5
soundHit: soundHit:
path: "/Audio/Weapons/Guns/Hits/taser_hit.ogg" path: "/Audio/Weapons/Guns/Hits/taser_hit.ogg"
soundForce: true forceSound: true
- type: StunOnCollide - type: StunOnCollide
stunAmount: 5 stunAmount: 5
knockdownAmount: 5 knockdownAmount: 5
@@ -256,7 +256,7 @@
Heat: 5 Heat: 5
soundHit: soundHit:
path: "/Audio/Weapons/tap.ogg" path: "/Audio/Weapons/tap.ogg"
soundForce: true forceSound: true
- type: entity - type: entity
name : disabler bolt practice name : disabler bolt practice
@@ -296,7 +296,7 @@
Heat: 1 Heat: 1
soundHit: soundHit:
path: "/Audio/Weapons/tap.ogg" path: "/Audio/Weapons/tap.ogg"
soundForce: true forceSound: true
- type: entity - type: entity
name: emitter bolt name: emitter bolt
@@ -865,7 +865,7 @@
Heat: 2 Heat: 2
soundHit: soundHit:
path: "/Audio/Weapons/tap.ogg" path: "/Audio/Weapons/tap.ogg"
soundForce: true forceSound: true
- type: entity - type: entity
name: tesla gun lightning name: tesla gun lightning

View File

@@ -13,13 +13,13 @@
map: [ "unshaded" ] map: [ "unshaded" ]
- type: Projectile - type: Projectile
deleteOnCollide: false deleteOnCollide: false
canPenetrate: true
impactEffect: null impactEffect: null
soundHit: soundHit:
path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg
damage: damage:
types: types:
Radiation: 25 Radiation: 25
- type: CanPenetrate
- type: Physics - type: Physics
- type: Fixtures - type: Fixtures
fixtures: fixtures:
@@ -64,5 +64,18 @@
state: particle0 state: particle0
shader: unshaded shader: unshaded
map: [ "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 - type: SinguloFood
energy: -10 energy: -10