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:
@@ -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))
|
||||||
{
|
{
|
||||||
|
|||||||
42
Content.Shared/Projectiles/CanPenetrateComponent.cs
Normal file
42
Content.Shared/Projectiles/CanPenetrateComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user