From e58d0313002350cd7d9ed24ea390e0cb5724a662 Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Tue, 4 Feb 2025 22:55:09 +0100 Subject: [PATCH] Hristov & .60 changes - Hristov Rework, Part 2 (#31662) * Initial commit * Updated values to reflect new resistances * Review fixes * Review fixes * LINQ BEGONETH --- .../Projectiles/ProjectileSystem.cs | 57 +++++++++++++++++-- .../SpeedModifiedOnWieldComponent.cs | 23 ++++++++ .../Projectiles/ProjectileComponent.cs | 23 +++++++- .../Projectiles/SharedProjectileSystem.cs | 2 +- .../Wieldable/SharedWieldableSystem.cs | 27 ++++++++- .../Ammunition/Projectiles/antimateriel.yml | 9 ++- .../Objects/Weapons/Guns/Snipers/snipers.yml | 11 ++++ 7 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 Content.Shared/Movement/Components/SpeedModifiedOnWieldComponent.cs diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 5cbc56b8fb..57601c3e9f 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -1,9 +1,11 @@ using Content.Server.Administration.Logs; +using Content.Server.Destructible; using Content.Server.Effects; using Content.Server.Weapons.Ranged.Systems; using Content.Shared.Camera; using Content.Shared.Damage; using Content.Shared.Database; +using Content.Shared.FixedPoint; using Content.Shared.Projectiles; using Robust.Shared.Physics.Events; using Robust.Shared.Player; @@ -15,6 +17,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly ColorFlashEffectSystem _color = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly DestructibleSystem _destructibleSystem = default!; [Dependency] private readonly GunSystem _guns = default!; [Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!; @@ -28,7 +31,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem { // This is so entities that shouldn't get a collision are ignored. if (args.OurFixtureId != ProjectileFixture || !args.OtherFixture.Hard - || component.DamagedEntity || component is { Weapon: null, OnlyCollideWhenShot: true }) + || component.ProjectileSpent || component is { Weapon: null, OnlyCollideWhenShot: true }) return; var target = args.OtherEntity; @@ -45,7 +48,13 @@ public sealed class ProjectileSystem : SharedProjectileSystem RaiseLocalEvent(uid, ref ev); var otherName = ToPrettyString(target); - var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: component.Shooter); + var damageRequired = _destructibleSystem.DestroyedAt(target); + if (TryComp(target, out var damageableComponent)) + { + damageRequired -= damageableComponent.TotalDamage; + damageRequired = FixedPoint2.Max(damageRequired, FixedPoint2.Zero); + } + var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, damageable: damageableComponent, origin: component.Shooter); var deleted = Deleted(target); if (modifiedDamage is not null && EntityManager.EntityExists(component.Shooter)) @@ -60,6 +69,46 @@ public sealed class ProjectileSystem : SharedProjectileSystem $"Projectile {ToPrettyString(uid):projectile} shot by {ToPrettyString(component.Shooter!.Value):user} hit {otherName:target} and dealt {modifiedDamage.GetTotal():damage} damage"); } + // If penetration is to be considered, we need to do some checks to see if the projectile should stop. + if (modifiedDamage is not null && component.PenetrationThreshold != 0) + { + // If a damage type is required, stop the bullet if the hit entity doesn't have that type. + if (component.PenetrationDamageTypeRequirement != null) + { + var stopPenetration = false; + foreach (var requiredDamageType in component.PenetrationDamageTypeRequirement) + { + if (!modifiedDamage.DamageDict.Keys.Contains(requiredDamageType)) + { + stopPenetration = true; + break; + } + } + if (stopPenetration) + component.ProjectileSpent = true; + } + + // If the object won't be destroyed, it "tanks" the penetration hit. + if (modifiedDamage.GetTotal() < damageRequired) + { + component.ProjectileSpent = true; + } + + if (!component.ProjectileSpent) + { + component.PenetrationAmount += damageRequired; + // The projectile has dealt enough damage to be spent. + if (component.PenetrationAmount >= component.PenetrationThreshold) + { + component.ProjectileSpent = true; + } + } + } + else + { + component.ProjectileSpent = true; + } + if (!deleted) { _guns.PlayImpactSound(target, modifiedDamage, component.SoundHit, component.ForceSound); @@ -68,9 +117,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem _sharedCameraRecoil.KickCamera(target, args.OurBody.LinearVelocity.Normalized()); } - component.DamagedEntity = true; - - if (component.DeleteOnCollide) + if (component.DeleteOnCollide && component.ProjectileSpent) QueueDel(uid); if (component.ImpactEffect != null && TryComp(uid, out TransformComponent? xform)) diff --git a/Content.Shared/Movement/Components/SpeedModifiedOnWieldComponent.cs b/Content.Shared/Movement/Components/SpeedModifiedOnWieldComponent.cs new file mode 100644 index 0000000000..01b02e60e6 --- /dev/null +++ b/Content.Shared/Movement/Components/SpeedModifiedOnWieldComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Wieldable; +using Robust.Shared.GameStates; + +namespace Content.Shared.Movement.Components; + +/// +/// Modifies the speed when an entity with this component is wielded. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedWieldableSystem)), AutoGenerateComponentState] +public sealed partial class SpeedModifiedOnWieldComponent : Component +{ + /// + /// How much the wielder's sprint speed is modified when the component owner is wielded. + /// + [DataField, AutoNetworkedField] + public float SprintModifier = 1f; + + /// + /// How much the wielder's walk speed is modified when the component owner is wielded. + /// + [DataField, AutoNetworkedField] + public float WalkModifier = 1f; +} diff --git a/Content.Shared/Projectiles/ProjectileComponent.cs b/Content.Shared/Projectiles/ProjectileComponent.cs index 8349252df2..9eee3767a6 100644 --- a/Content.Shared/Projectiles/ProjectileComponent.cs +++ b/Content.Shared/Projectiles/ProjectileComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Damage; +using Content.Shared.FixedPoint; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -75,8 +76,26 @@ public sealed partial class ProjectileComponent : Component public bool OnlyCollideWhenShot = false; /// - /// Whether this projectile has already damaged an entity. + /// If true, the projectile has hit enough targets and should no longer interact with further collisions pending deletion. /// [DataField] - public bool DamagedEntity; + public bool ProjectileSpent; + + /// + /// When a projectile has this threshold set, it will continue to penetrate entities until the damage dealt reaches this threshold. + /// + [DataField] + public FixedPoint2 PenetrationThreshold = FixedPoint2.Zero; + + /// + /// If set, the projectile will not penetrate objects that lack the ability to take these damage types. + /// + [DataField] + public List? PenetrationDamageTypeRequirement; + + /// + /// Tracks the amount of damage dealt for penetration purposes. + /// + [DataField] + public FixedPoint2 PenetrationAmount = FixedPoint2.Zero; } diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 85e75d6d29..bca9b36f89 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -79,7 +79,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem { projectile.Shooter = null; projectile.Weapon = null; - projectile.DamagedEntity = false; + projectile.ProjectileSpent = false; } // Land it just coz uhhh yeah diff --git a/Content.Shared/Wieldable/SharedWieldableSystem.cs b/Content.Shared/Wieldable/SharedWieldableSystem.cs index 24c63c5f2f..110c941dac 100644 --- a/Content.Shared/Wieldable/SharedWieldableSystem.cs +++ b/Content.Shared/Wieldable/SharedWieldableSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Interaction.Events; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Item; using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; using Content.Shared.Popups; using Content.Shared.Timing; using Content.Shared.Verbs; @@ -27,6 +28,7 @@ namespace Content.Shared.Wieldable; public abstract class SharedWieldableSystem : EntitySystem { + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; @@ -56,6 +58,9 @@ public abstract class SharedWieldableSystem : EntitySystem SubscribeLocalEvent(OnGunUnwielded); SubscribeLocalEvent(OnGunRefreshModifiers); SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnSpeedModifierWielded); + SubscribeLocalEvent(OnSpeedModifierUnwielded); + SubscribeLocalEvent>(OnRefreshSpeedWielded); SubscribeLocalEvent(OnGetMeleeDamage); } @@ -119,9 +124,29 @@ public abstract class SharedWieldableSystem : EntitySystem } } + private void OnSpeedModifierWielded(EntityUid uid, SpeedModifiedOnWieldComponent component, ItemWieldedEvent args) + { + if (args.User != null) + _movementSpeedModifier.RefreshMovementSpeedModifiers(args.User); + } + + private void OnSpeedModifierUnwielded(EntityUid uid, SpeedModifiedOnWieldComponent component, ItemUnwieldedEvent args) + { + if (args.User != null) + _movementSpeedModifier.RefreshMovementSpeedModifiers(args.User); + } + + private void OnRefreshSpeedWielded(EntityUid uid, SpeedModifiedOnWieldComponent component, ref HeldRelayedEvent args) + { + if (TryComp(uid, out var wield) && wield.Wielded) + { + args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); + } + } + private void OnExamineRequires(Entity entity, ref ExaminedEvent args) { - if(entity.Comp.WieldRequiresExamineMessage != null) + if (entity.Comp.WieldRequiresExamineMessage != null) args.PushText(Loc.GetString(entity.Comp.WieldRequiresExamineMessage)); } 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 65b7dbc165..7556f2c371 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimateriel.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimateriel.yml @@ -7,7 +7,10 @@ - type: Projectile damage: types: - Piercing: 40 - Structural: 30 + Piercing: 75 + Structural: 226 + penetrationThreshold: 360 + penetrationDamageTypeRequirement: + - Structural - type: StaminaDamageOnCollide - damage: 35 + damage: 60 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml index cca3a6f302..23dc96c49f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml @@ -57,12 +57,23 @@ sprite: Objects/Weapons/Guns/Snipers/heavy_sniper.rsi - type: Clothing sprite: Objects/Weapons/Guns/Snipers/heavy_sniper.rsi + - type: GunRequiresWield + - type: Gun + fireRate: 0.4 + selectedMode: SemiAuto + availableModes: + - SemiAuto + soundGunshot: + path: /Audio/Weapons/Guns/Gunshots/sniper.ogg - type: BallisticAmmoProvider whitelist: tags: - CartridgeAntiMateriel capacity: 5 proto: CartridgeAntiMateriel + - type: SpeedModifiedOnWield + walkModifier: 0.25 + sprintModifier: 0.25 - type: CursorOffsetRequiresWield - type: EyeCursorOffset maxOffset: 3 -- 2.51.2