diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index 9e50cab3e10..871a90d4dda 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -185,6 +185,28 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? { userImpulse = true; + // Получение и модификация свойств снаряда на основе параметров оружия + foreach (var (ammoEntity, shootable) in ammo) + { + if (shootable is ProjectileComponent projectile && EntityManager.TryGetComponent(gunUid, out var gunComp)) + { + // Применение модификаторов проникающей способности + if (EntityManager.TryGetComponent(ammoEntity, out var penetration) && + gunComp.PenetrationModifier != null) + { + penetration.PenetrationPower += gunComp.PenetrationModifier.Value; + } + + // Применение модификатора урона + if (gunComp.DamageMultiplier != null) + { + projectile.Damage *= gunComp.DamageMultiplier.Value; + } + + projectile.GunModifiersApplied = true; + } + } + // Rather than splitting client / server for every ammo provider it's easier // to just delete the spawned entities. This is for programmer sanity despite the wasted perf. // This also means any ammo specific stuff can be grabbed as necessary. diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 372dc8a75d1..90132882232 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -159,8 +159,69 @@ private void OnAttemptPacifiedThrow(Entity ent, r { 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 canPenetrate)) + return; + + //Delete the projectile if it has no penetration power left. + if (canPenetrate.PenetrationPower <= 0) + { + QueueDel(uid); + 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 (canPenetrate.PenetrationLayer != null) + { + if (args.Fixture.CollisionLayer > (int) canPenetrate.PenetrationLayer) + { + QueueDel(uid); + return; + } + } + + //Allow the projectile to deal damage again. + if(canPenetrate.DamageAfterCollide) + component.DamagedEntity = false; + + //If the projectile has a limit on the amount of penetrations, reduce it. + if (canPenetrate.PenetrationPower != null) + { + //If it's CollisonLayer is higher than the MachineLayer it's a "hard" target and + //deducts more penetration power. + if (args.Fixture.CollisionLayer > (int) CollisionGroup.MachineLayer) + canPenetrate.PenetrationPower -= 1; + + //If the target's CollisonLayer is equal to or lower than the MachineLayer, + //it's a "soft" target and deducts less penetration power. + else + canPenetrate.PenetrationPower -= 0.5f; + + // Delete the projectile if it collides with an entity that it doesn't have enough penetration power for. + if (canPenetrate.PenetrationPower < 0) + { + QueueDel(uid); + return; + } + } + //Apply the penetration damage modifier if the projectile has one. + if (canPenetrate.DamageModifier != null) + component.Damage *= canPenetrate.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] public sealed class ImpactEffectEvent : EntityEventArgs { @@ -174,6 +235,22 @@ public ImpactEffectEvent(string prototype, NetCoordinates coordinates) } } +[Serializable, NetSerializable] +public class AfterProjectileHitEvent : EntityEventArgs +{ + public EntityUid ProjectileId { get; set; } + public EntityUid TargetId { get; set; } + public bool ShouldPenetrate { get; set; } + + public AfterProjectileHitEvent(EntityUid projectileId, EntityUid targetId, bool shouldPenetrate) + { + ProjectileId = projectileId; + TargetId = targetId; + ShouldPenetrate = shouldPenetrate; + } +} + + /// /// Raised when an entity is just about to be hit with a projectile but can reflect it /// diff --git a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs index e8b899c4708..2ec67c50688 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs @@ -228,6 +228,18 @@ public sealed partial class GunComponent : Component /// [DataField] public bool ClumsyProof = false; + + /// + /// The amount of penetrations the gun adds to projectiles shot by the gun. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float? PenetrationModifier; + + /// + /// The damage multiplier added to projectiles shot by the gun. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float? DamageMultiplier; } [Flags] diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimateriel.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimateriel.yml index 31d7b65fe8b..8b8d89f907b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimateriel.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimateriel.yml @@ -9,5 +9,7 @@ types: Piercing: 40 Structural: 30 + - type: CanPenetrate + penetrationPower: 0 - type: StaminaDamageOnCollide damage: 35 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml index a45924fd4c5..27e0e71cbfb 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml @@ -19,6 +19,7 @@ - suitStorage - type: AmmoCounter - type: Gun + penetrationModifier: 1.5 #3 mobs or 1 wall and 1 mob. fireRate: 0.75 selectedMode: SemiAuto availableModes: