diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 80c5f039d3..15ea5936e1 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -28,14 +28,10 @@ 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.DamagedEntity || component is { Weapon: null, OnlyCollideWhenShot: true }) return; - } var target = args.OtherEntity; - // it's here so this check is only done once before possible hit var attemptEv = new ProjectileReflectAttemptEvent(uid, component, false); RaiseLocalEvent(target, ref attemptEv); @@ -45,26 +41,11 @@ public sealed class ProjectileSystem : SharedProjectileSystem return; } - if (TryHandleProjectile(target, (uid, component))) - { - var direction = args.OurBody.LinearVelocity.Normalized(); - _sharedCameraRecoil.KickCamera(target, direction); - } - } - - /// - /// Tries to handle a projectile interacting with the target. - /// - /// True if the target isn't deleted. - public bool TryHandleProjectile(EntityUid target, Entity projectile) - { - var uid = projectile.Owner; - var component = projectile.Comp; - var ev = new ProjectileHitEvent(component.Damage, target, component.Shooter); RaiseLocalEvent(uid, ref ev); var otherName = ToPrettyString(target); + var direction = args.OurBody.LinearVelocity.Normalized(); var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: component.Shooter); var deleted = Deleted(target); @@ -83,11 +64,12 @@ public sealed class ProjectileSystem : SharedProjectileSystem if (!deleted) { _guns.PlayImpactSound(target, modifiedDamage, component.SoundHit, component.ForceSound); + _sharedCameraRecoil.KickCamera(target, direction); } component.DamagedEntity = true; - var afterProjectileHitEvent = new AfterProjectileHitEvent(component.Damage, target); + var afterProjectileHitEvent = new AfterProjectileHitEvent(component.Damage, target, args.OtherFixture); RaiseLocalEvent(uid, ref afterProjectileHitEvent); if (component.DeleteOnCollide) @@ -97,7 +79,5 @@ public sealed class ProjectileSystem : SharedProjectileSystem { RaiseNetworkEvent(new ImpactEffectEvent(component.ImpactEffect, GetNetCoordinates(xform.Coordinates)), Filter.Pvs(xform.Coordinates, entityMan: EntityManager)); } - - return !deleted; } } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index fd3b6c5db3..b8f8f12211 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -4,7 +4,6 @@ using Content.Server.Administration.Logs; using Content.Server.Cargo.Systems; using Content.Server.Interaction; using Content.Server.Power.EntitySystems; -using Content.Server.Projectiles; using Content.Server.Stunnable; using Content.Server.Weapons.Ranged.Components; using Content.Shared.Damage; @@ -30,13 +29,13 @@ namespace Content.Server.Weapons.Ranged.Systems; public sealed partial class GunSystem : SharedGunSystem { + [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly DamageExamineSystem _damageExamine = default!; [Dependency] private readonly InteractionSystem _interaction = default!; [Dependency] private readonly PricingSystem _pricing = default!; [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; - [Dependency] private readonly ProjectileSystem _projectile = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly StunSystem _stun = default!; @@ -66,137 +65,6 @@ public sealed partial class GunSystem : SharedGunSystem args.Price += price * component.UnspawnedCount; } - protected override bool ShootDirect(EntityUid gunUid, GunComponent gun, EntityUid target, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityUid user) - { - var result = false; - - // TODO: This is dogshit. I just want to get executions slightly better. - // Ideally you'd pull out cartridge + ammo to separate handling functions and re-use it here, then hitscan you need to bypass entirely. - // You should also make shooting into a struct of args given how many there are now. - var fromCoordinates = Transform(gunUid).Coordinates; - var toCoordinates = Transform(target).Coordinates; - - var fromMap = fromCoordinates.ToMap(EntityManager, TransformSystem); - var toMap = toCoordinates.ToMapPos(EntityManager, TransformSystem); - var mapDirection = toMap - fromMap.Position; - var angle = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle()); - - // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. - var fromEnt = MapManager.TryFindGridAt(fromMap, out var gridUid, out _) - ? fromCoordinates.WithEntityId(gridUid, EntityManager) - : new EntityCoordinates(MapManager.GetMapEntityId(fromMap.MapId), fromMap.Position); - - // I must be high because this was getting tripped even when true. - // DebugTools.Assert(direction != Vector2.Zero); - var shotProjectiles = new List(ammo.Count); - var cartridgeBullets = new List(); - - foreach (var (ent, shootable) in ammo) - { - switch (shootable) - { - // Cartridge shoots something else - case CartridgeAmmoComponent cartridge: - if (!cartridge.Spent) - { - for (var i = 0; i < cartridge.Count; i++) - { - var uid = Spawn(cartridge.Prototype, fromEnt); - cartridgeBullets.Add(uid); - } - - RaiseLocalEvent(ent!.Value, new AmmoShotEvent() - { - FiredProjectiles = cartridgeBullets, - }); - - shotProjectiles.AddRange(cartridgeBullets); - cartridgeBullets.Clear(); - SetCartridgeSpent(ent.Value, cartridge, true); - MuzzleFlash(gunUid, cartridge, user); - Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); - - if (cartridge.DeleteOnSpawn) - Del(ent.Value); - } - else - { - Audio.PlayPredicted(gun.SoundEmpty, gunUid, user); - } - - // Something like ballistic might want to leave it in the container still - if (!cartridge.DeleteOnSpawn && !Containers.IsEntityInContainer(ent!.Value)) - EjectCartridge(ent.Value, angle); - - result = true; - Dirty(ent!.Value, cartridge); - break; - // Ammo shoots itself - case AmmoComponent newAmmo: - result = true; - shotProjectiles.Add(ent!.Value); - MuzzleFlash(gunUid, newAmmo, user); - Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); - break; - case HitscanPrototype hitscan: - result = true; - var hitEntity = target; - if (hitscan.StaminaDamage > 0f) - _stamina.TakeStaminaDamage(hitEntity, hitscan.StaminaDamage, source: user); - - var dmg = hitscan.Damage; - - var hitName = ToPrettyString(hitEntity); - if (dmg != null) - dmg = Damageable.TryChangeDamage(hitEntity, dmg, origin: user); - - // check null again, as TryChangeDamage returns modified damage values - if (dmg != null) - { - if (!Deleted(hitEntity)) - { - if (dmg.Any()) - { - _color.RaiseEffect(Color.Red, new List() { hitEntity }, Filter.Pvs(hitEntity, entityManager: EntityManager)); - } - - // TODO get fallback position for playing hit sound. - PlayImpactSound(hitEntity, dmg, hitscan.Sound, hitscan.ForceSound); - } - - Logs.Add(LogType.HitScanHit, - $"{ToPrettyString(user):user} hit {hitName:target} using hitscan and dealt {dmg.GetTotal():damage} damage"); - } - - Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - - foreach (var ammoUid in shotProjectiles) - { - // TODO: Handle this shit - if (!TryComp(ammoUid, out ProjectileComponent? projectileComponent)) - { - QueueDel(ammoUid); - continue; - } - - _projectile.TryHandleProjectile(target, (ammoUid, projectileComponent)); - // Even this deletion handling is mega sussy. - Del(ammoUid); - } - - RaiseLocalEvent(gunUid, new AmmoShotEvent() - { - FiredProjectiles = shotProjectiles, - }); - - return result; - } - public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, out bool userImpulse, EntityUid? user = null, bool throwItems = false) { @@ -204,7 +72,7 @@ public sealed partial class GunSystem : SharedGunSystem // Try a clumsy roll // TODO: Who put this here - if (TryComp(user, out var clumsy) && !gun.ClumsyProof) + if (TryComp(user, out var clumsy) && gun.ClumsyProof == false) { for (var i = 0; i < ammo.Count; i++) { @@ -225,8 +93,6 @@ public sealed partial class GunSystem : SharedGunSystem } } - // As the above message wasn't obvious stop putting stuff here and use events - var fromMap = fromCoordinates.ToMap(EntityManager, TransformSystem); var toMap = toCoordinates.ToMapPos(EntityManager, TransformSystem); var mapDirection = toMap - fromMap.Position; @@ -234,7 +100,7 @@ public sealed partial class GunSystem : SharedGunSystem var angle = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle()); // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. - var fromEnt = MapManager.TryFindGridAt(fromMap, out var gridUid, out _) + var fromEnt = MapManager.TryFindGridAt(fromMap, out var gridUid, out var grid) ? fromCoordinates.WithEntityId(gridUid, EntityManager) : new EntityCoordinates(MapManager.GetMapEntityId(fromMap.MapId), fromMap.Position); @@ -246,7 +112,6 @@ public sealed partial class GunSystem : SharedGunSystem // I must be high because this was getting tripped even when true. // DebugTools.Assert(direction != Vector2.Zero); var shotProjectiles = new List(ammo.Count); - var cartridgeBullets = new List(); foreach (var (ent, shootable) in ammo) { @@ -275,23 +140,21 @@ public sealed partial class GunSystem : SharedGunSystem { var uid = Spawn(cartridge.Prototype, fromEnt); ShootOrThrow(uid, angles[i].ToVec(), gunVelocity, gun, gunUid, user); - cartridgeBullets.Add(uid); + shotProjectiles.Add(uid); } } else { var uid = Spawn(cartridge.Prototype, fromEnt); ShootOrThrow(uid, mapDirection, gunVelocity, gun, gunUid, user); - cartridgeBullets.Add(uid); + shotProjectiles.Add(uid); } RaiseLocalEvent(ent!.Value, new AmmoShotEvent() { - FiredProjectiles = cartridgeBullets, + FiredProjectiles = shotProjectiles, }); - shotProjectiles.AddRange(cartridgeBullets); - cartridgeBullets.Clear(); SetCartridgeSpent(ent.Value, cartridge, true); MuzzleFlash(gunUid, cartridge, user); Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); diff --git a/Content.Shared/Execution/DoafterEvent.cs b/Content.Shared/Execution/DoafterEvent.cs deleted file mode 100644 index 7854974527..0000000000 --- a/Content.Shared/Execution/DoafterEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Shared.DoAfter; -using Robust.Shared.Serialization; - -namespace Content.Shared.Execution; - -[Serializable, NetSerializable] -public sealed partial class ExecutionDoAfterEvent : SimpleDoAfterEvent -{ -} diff --git a/Content.Shared/Execution/ExecutionComponent.cs b/Content.Shared/Execution/ExecutionComponent.cs deleted file mode 100644 index f9c5111d63..0000000000 --- a/Content.Shared/Execution/ExecutionComponent.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Execution; - -/// -/// Added to entities that can be used to execute another target. -/// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -public sealed partial class ExecutionComponent : Component -{ - /// - /// How long the execution duration lasts. - /// - [DataField, AutoNetworkedField] - public float DoAfterDuration = 5f; - - [DataField, AutoNetworkedField] - public float DamageModifier = 9f; - - // Not networked because this is transient inside of a tick. - /// - /// True if it is currently executing for handlers. - /// - [DataField] - public bool Executing = true; -} diff --git a/Content.Shared/Execution/ExecutionSystem.cs b/Content.Shared/Execution/ExecutionSystem.cs deleted file mode 100644 index de6db205be..0000000000 --- a/Content.Shared/Execution/ExecutionSystem.cs +++ /dev/null @@ -1,241 +0,0 @@ -using Content.Shared.Weapons.Ranged.Systems; -using Content.Shared.ActionBlocker; -using Content.Shared.CombatMode; -using Content.Shared.Damage; -using Content.Shared.Database; -using Content.Shared.DoAfter; -using Content.Shared.Mobs.Components; -using Content.Shared.Mobs.Systems; -using Content.Shared.Popups; -using Content.Shared.Verbs; -using Content.Shared.Weapons.Melee; -using Content.Shared.Weapons.Melee.Events; -using Content.Shared.Weapons.Ranged.Components; -using Robust.Shared.Network; -using Robust.Shared.Player; - -namespace Content.Shared.Execution; - -/// -/// Verb for violently murdering cuffed creatures. -/// -public sealed class ExecutionSystem : EntitySystem -{ - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - [Dependency] private readonly SharedGunSystem _gunSystem = default!; - [Dependency] private readonly SharedCombatModeSystem _combatSystem = default!; - [Dependency] private readonly SharedMeleeWeaponSystem _meleeSystem = default!; - - // TODO: Still needs more cleaning up. - private const string DefaultInternalMeleeExecutionMessage = "execution-popup-melee-initial-internal"; - private const string DefaultExternalMeleeExecutionMessage = "execution-popup-melee-initial-external"; - private const string DefaultCompleteInternalMeleeExecutionMessage = "execution-popup-melee-complete-internal"; - private const string DefaultCompleteExternalMeleeExecutionMessage = "execution-popup-melee-complete-external"; - private const string DefaultInternalGunExecutionMessage = "execution-popup-gun-initial-internal"; - private const string DefaultExternalGunExecutionMessage = "execution-popup-gun-initial-external"; - private const string DefaultCompleteInternalGunExecutionMessage = "execution-popup-gun-complete-internal"; - private const string DefaultCompleteExternalGunExecutionMessage = "execution-popup-gun-complete-external"; - - /// - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent>(OnGetInteractionsVerbs); - SubscribeLocalEvent(OnExecutionDoAfter); - SubscribeLocalEvent(OnGetMeleeDamage); - } - - private void OnGetInteractionsVerbs(EntityUid uid, ExecutionComponent comp, GetVerbsEvent args) - { - if (args.Hands == null || args.Using == null || !args.CanAccess || !args.CanInteract) - return; - - var attacker = args.User; - var weapon = args.Using.Value; - var victim = args.Target; - - if (!CanExecuteWithAny(victim, attacker)) - return; - - UtilityVerb verb = new() - { - Act = () => TryStartExecutionDoAfter(weapon, victim, attacker, comp), - Impact = LogImpact.High, - Text = Loc.GetString("execution-verb-name"), - Message = Loc.GetString("execution-verb-message"), - }; - - args.Verbs.Add(verb); - } - - private void TryStartExecutionDoAfter(EntityUid weapon, EntityUid victim, EntityUid attacker, ExecutionComponent comp) - { - if (!CanExecuteWithAny(victim, attacker)) - return; - - // TODO: This should just be on the weapons as a single execution message. - var defaultExecutionInternal = DefaultInternalMeleeExecutionMessage; - var defaultExecutionExternal = DefaultExternalMeleeExecutionMessage; - - if (HasComp(weapon)) - { - defaultExecutionExternal = DefaultInternalGunExecutionMessage; - defaultExecutionInternal = DefaultExternalGunExecutionMessage; - } - - var internalMsg = defaultExecutionInternal; - var externalMsg = defaultExecutionExternal; - ShowExecutionInternalPopup(internalMsg, attacker, victim, weapon); - ShowExecutionExternalPopup(externalMsg, attacker, victim, weapon); - - var doAfter = - new DoAfterArgs(EntityManager, attacker, comp.DoAfterDuration, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) - { - BreakOnTargetMove = true, - BreakOnUserMove = true, - BreakOnDamage = true, - NeedHand = true - }; - - _doAfterSystem.TryStartDoAfter(doAfter); - - } - - private bool CanExecuteWithAny(EntityUid victim, EntityUid attacker) - { - // Use suicide. - if (victim == attacker) - return false; - - // No point executing someone if they can't take damage - if (!TryComp(victim, out _)) - return false; - - // You can't execute something that cannot die - if (!TryComp(victim, out var mobState)) - return false; - - // You're not allowed to execute dead people (no fun allowed) - if (_mobStateSystem.IsDead(victim, mobState)) - return false; - - // You must be able to attack people to execute - if (!_actionBlockerSystem.CanAttack(attacker, victim)) - return false; - - // The victim must be incapacitated to be executed - if (victim != attacker && _actionBlockerSystem.CanInteract(victim, null)) - return false; - - // All checks passed - return true; - } - - private void OnExecutionDoAfter(EntityUid uid, ExecutionComponent component, ExecutionDoAfterEvent args) - { - if (args.Handled || args.Cancelled || args.Used == null || args.Target == null) - return; - - var attacker = args.User; - var victim = args.Target.Value; - var weapon = args.Used.Value; - - if (!CanExecuteWithAny(victim, attacker)) - return; - - // This is needed so the melee system does not stop it. - var prev = _combatSystem.IsInCombatMode(attacker); - _combatSystem.SetInCombatMode(attacker, true); - component.Executing = true; - string? internalMsg = null; - string? externalMsg = null; - - if (TryComp(uid, out MeleeWeaponComponent? melee)) - { - _meleeSystem.AttemptLightAttack(attacker, weapon, melee, victim); - internalMsg = DefaultCompleteInternalMeleeExecutionMessage; - externalMsg = DefaultCompleteExternalMeleeExecutionMessage; - } - else if (TryComp(uid, out GunComponent? gun)) - { - var clumsyShot = false; - - // TODO: This should just be an event or something instead to get this. - // TODO: Handle clumsy. - if (!_gunSystem.AttemptDirectShoot(args.User, uid, args.Target.Value, gun)) - { - internalMsg = null; - externalMsg = null; - } - else - { - internalMsg = DefaultCompleteInternalGunExecutionMessage; - externalMsg = DefaultCompleteExternalGunExecutionMessage; - } - args.Handled = true; - } - - _combatSystem.SetInCombatMode(attacker, prev); - component.Executing = false; - args.Handled = true; - - if (internalMsg != null && externalMsg != null) - { - ShowExecutionInternalPopup(internalMsg, attacker, victim, uid); - ShowExecutionExternalPopup(externalMsg, attacker, victim, uid); - } - } - - private void OnGetMeleeDamage(EntityUid uid, ExecutionComponent comp, ref GetMeleeDamageEvent args) - { - if (!TryComp(uid, out var melee) || - !TryComp(uid, out var execComp) || - !execComp.Executing) - { - return; - } - - var bonus = melee.Damage * execComp.DamageModifier - melee.Damage; - args.Damage += bonus; - } - - private void ShowExecutionInternalPopup(string locString, - EntityUid attacker, EntityUid victim, EntityUid weapon, bool predict = true) - { - if (predict) - { - _popupSystem.PopupClient( - Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), - attacker, - attacker, - PopupType.Medium - ); - } - else - { - _popupSystem.PopupEntity( - Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), - attacker, - Filter.Entities(attacker), - true, - PopupType.Medium - ); - } - - } - - private void ShowExecutionExternalPopup(string locString, EntityUid attacker, EntityUid victim, EntityUid weapon) - { - _popupSystem.PopupEntity( - Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), - attacker, - Filter.PvsExcept(attacker), - true, - PopupType.MediumCaution - ); - } -} diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 027dc03939..f57e873653 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -34,6 +34,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(PreventCollision); + SubscribeLocalEvent(AfterProjectileHit); SubscribeLocalEvent(OnEmbedProjectileHit); SubscribeLocalEvent(OnEmbedThrowDoHit); SubscribeLocalEvent(OnEmbedActivate); @@ -162,6 +163,18 @@ public abstract partial class SharedProjectileSystem : EntitySystem { 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) + { + //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] @@ -193,4 +206,4 @@ public record struct ProjectileHitEvent(DamageSpecifier Damage, EntityUid Target /// Raised after a projectile has dealt it's damage. /// [ByRefEvent] -public record struct AfterProjectileHitEvent(DamageSpecifier Damage, EntityUid Target); +public record struct AfterProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, Fixture Fixture); diff --git a/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs b/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs index 6325d95330..40925ad614 100644 --- a/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs +++ b/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs @@ -19,7 +19,7 @@ public record struct ShotAttemptedEvent public bool Cancelled { get; private set; } - /// + /// /// Prevent the gun from shooting /// public void Cancel() @@ -27,7 +27,7 @@ public record struct ShotAttemptedEvent Cancelled = true; } - /// + /// /// Allow the gun to shoot again, only use if you know what you are doing /// public void Uncancel() diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 2132dd2631..ba22ba2cdc 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -21,7 +21,6 @@ using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; -using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -144,7 +143,7 @@ public abstract partial class SharedGunSystem : EntitySystem gun.ShootCoordinates = GetCoordinates(msg.Coordinates); Log.Debug($"Set shoot coordinates to {gun.ShootCoordinates}"); - AttemptShootInternal(user.Value, ent, gun); + AttemptShoot(user.Value, ent, gun); } private void OnStopShootRequest(RequestStopShootEvent ev, EntitySessionEventArgs args) @@ -208,38 +207,13 @@ public abstract partial class SharedGunSystem : EntitySystem Dirty(uid, gun); } - /// - /// Attempts to shoot the specified target directly. - /// This may bypass projectiles firing etc. - /// - public bool AttemptDirectShoot(EntityUid user, EntityUid gunUid, EntityUid target, GunComponent gun) - { - // Unique name so people don't think it's "shoot towards" and not "I will teleport a bullet into them". - gun.ShootCoordinates = Transform(target).Coordinates; - - if (!TryTakeAmmo(user, gunUid, gun, out _, out _, out var args)) - { - gun.ShootCoordinates = null; - return false; - } - - var result = ShootDirect(gunUid, gun, target, args.Ammo, user: user); - gun.ShootCoordinates = null; - return result; - } - - protected virtual bool ShootDirect(EntityUid gunUid, GunComponent gun, EntityUid target, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityUid user) - { - return false; - } - /// /// Attempts to shoot at the target coordinates. Resets the shot counter after every shot. /// public void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, EntityCoordinates toCoordinates) { gun.ShootCoordinates = toCoordinates; - AttemptShootInternal(user, gunUid, gun); + AttemptShoot(user, gunUid, gun); gun.ShotCounter = 0; } @@ -250,35 +224,20 @@ public abstract partial class SharedGunSystem : EntitySystem { var coordinates = new EntityCoordinates(gunUid, new Vector2(0, -1)); gun.ShootCoordinates = coordinates; - AttemptShootInternal(gunUid, gunUid, gun); + AttemptShoot(gunUid, gunUid, gun); gun.ShotCounter = 0; } - private void AttemptShootInternal(EntityUid user, EntityUid gunUid, GunComponent gun) - { - if (!TryTakeAmmo(user, gunUid, gun, out var fromCoordinates, out var toCoordinates, out var args)) - return; - - Shoot(gunUid, gun, args.Ammo, fromCoordinates, toCoordinates, out var userImpulse, user: user); - - if (userImpulse && TryComp(user, out var userPhysics)) - { - if (_gravity.IsWeightless(user, userPhysics)) - CauseImpulse(fromCoordinates, toCoordinates, user, userPhysics); - } - } - - /// - /// Validates if a gun can currently shoot. - /// - [Pure] - private bool CanShoot(EntityUid user, EntityUid gunUid, GunComponent gun) + private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) { if (gun.FireRateModified <= 0f || !_actionBlockerSystem.CanAttack(user)) - { - return false; - } + return; + + var toCoordinates = gun.ShootCoordinates; + + if (toCoordinates == null) + return; var curTime = Timing.CurTime; @@ -290,42 +249,17 @@ public abstract partial class SharedGunSystem : EntitySystem }; RaiseLocalEvent(gunUid, ref prevention); if (prevention.Cancelled) - return false; + return; RaiseLocalEvent(user, ref prevention); if (prevention.Cancelled) - return false; + return; // Need to do this to play the clicking sound for empty automatic weapons // but not play anything for burst fire. if (gun.NextFire > curTime) - return false; + return; - return true; - } - - /// - /// Tries to return ammo prepped for shooting if a gun is available to shoot. - /// - private bool TryTakeAmmo( - EntityUid user, - EntityUid gunUid, GunComponent gun, - out EntityCoordinates fromCoordinates, - out EntityCoordinates toCoordinates, - [NotNullWhen(true)] out TakeAmmoEvent? args) - { - toCoordinates = EntityCoordinates.Invalid; - fromCoordinates = EntityCoordinates.Invalid; - args = null; - - if (!CanShoot(user, gunUid, gun)) - return false; - - if (gun.ShootCoordinates == null) - return false; - - toCoordinates = gun.ShootCoordinates.Value; - var curTime = Timing.CurTime; var fireRate = TimeSpan.FromSeconds(1f / gun.FireRateModified); // First shot @@ -373,11 +307,10 @@ public abstract partial class SharedGunSystem : EntitySystem } gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds)); - return false; + return; } - fromCoordinates = Transform(user).Coordinates; - + var fromCoordinates = Transform(user).Coordinates; // Remove ammo var ev = new TakeAmmoEvent(shots, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, user); @@ -412,18 +345,24 @@ public abstract partial class SharedGunSystem : EntitySystem // May cause prediction issues? Needs more tweaking gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds)); Audio.PlayPredicted(gun.SoundEmpty, gunUid, user); - return false; + return; } - return false; + return; } // Shoot confirmed - sounds also played here in case it's invalid (e.g. cartridge already spent). + Shoot(gunUid, gun, ev.Ammo, fromCoordinates, toCoordinates.Value, out var userImpulse, user, throwItems: attemptEv.ThrowItems); var shotEv = new GunShotEvent(user, ev.Ammo); RaiseLocalEvent(gunUid, ref shotEv); - args = ev; - return true; + if (userImpulse && TryComp(user, out var userPhysics)) + { + if (_gravity.IsWeightless(user, userPhysics)) + CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics); + } + + Dirty(gunUid, gun); } public void Shoot( diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl deleted file mode 100644 index 5bd4613e8c..0000000000 --- a/Resources/Locale/en-US/execution/execution.ftl +++ /dev/null @@ -1,20 +0,0 @@ -execution-verb-name = Execute -execution-verb-message = Use your weapon to execute someone. - -# All the below localisation strings have access to the following variables -# attacker (the person committing the execution) -# victim (the person being executed) -# weapon (the weapon used for the execution) - -execution-popup-gun-initial-internal = You ready the muzzle of {THE($weapon)} against {$victim}'s head. -execution-popup-gun-initial-external = {$attacker} readies the muzzle of {THE($weapon)} against {$victim}'s head. -execution-popup-gun-complete-internal = You blast {$victim} in the head! -execution-popup-gun-complete-external = {$attacker} blasts {$victim} in the head! -execution-popup-gun-clumsy-internal = You miss {$victim}'s head and shoot your foot instead! -execution-popup-gun-clumsy-external = {$attacker} misses {$victim} and shoots {POSS-ADJ($attacker)} foot instead! -execution-popup-gun-empty = {CAPITALIZE(THE($weapon))} clicks. - -execution-popup-melee-initial-internal = You ready {THE($weapon)} against {$victim}'s throat. -execution-popup-melee-initial-external = {$attacker} readies {POSS-ADJ($attacker)} {$weapon} against the throat of {$victim}. -execution-popup-melee-complete-internal = You slit the throat of {$victim}! -execution-popup-melee-complete-external = {$attacker} slits the throat of {$victim}! diff --git a/Resources/Locale/en-US/kitchen/components/butcherable-component.ftl b/Resources/Locale/en-US/kitchen/components/butcherable-component.ftl index 4a83cd455d..ff28cc44db 100644 --- a/Resources/Locale/en-US/kitchen/components/butcherable-component.ftl +++ b/Resources/Locale/en-US/kitchen/components/butcherable-component.ftl @@ -1,4 +1,4 @@ -butcherable-different-tool = You need a different tool to butcher { THE($target) }. +butcherable-different-tool = You are going to need a different tool to butcher { THE($target) }. butcherable-knife-butchered-success = You butcher { THE($target) } with { THE($knife) }. butcherable-need-knife = Use a sharp object to butcher { THE($target) }. butcherable-not-in-container = { CAPITALIZE(THE($target)) } can't be in a container. diff --git a/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml b/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml index a86965f96f..62468b4615 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml @@ -6,8 +6,6 @@ description: A small piece of crystal. components: - type: Sharp - - type: Execution - doAfterDuration: 4.0 - type: Sprite layers: - sprite: Objects/Materials/Shards/crystal.rsi diff --git a/Resources/Prototypes/Entities/Objects/Materials/shards.yml b/Resources/Prototypes/Entities/Objects/Materials/shards.yml index 22d5cdefe4..fa57f90c18 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/shards.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/shards.yml @@ -5,8 +5,6 @@ description: It's a shard of some unknown material. components: - type: Sharp - - type: Execution - doAfterDuration: 4.0 - type: Sprite layers: - sprite: Objects/Materials/Shards/shard.rsi diff --git a/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml b/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml index 98bc92968b..b7c73f5e0c 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml @@ -5,8 +5,6 @@ description: In Space Glasgow this is called a conversation starter. components: - type: Sharp - - type: Execution - doAfterDuration: 4.0 - type: MeleeWeapon attackRate: 1.5 damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml index 236ed20681..9d685e1ddc 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml @@ -19,7 +19,6 @@ path: /Audio/Weapons/Guns/Empty/lmg_empty.ogg - type: StaticPrice price: 500 - - type: Execution # No chamber because HMG may want its own - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml index 499a950e78..49b2eeaada 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml @@ -60,7 +60,6 @@ price: 500 - type: UseDelay delay: 1 - - type: Execution - type: entity name: L6 SAW diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index 80de02c6da..8b31bf40ed 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -19,7 +19,6 @@ containers: ballistic-ammo: !type:Container ents: [] - - type: Execution - type: entity name: china lake diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml index 2b2c6fe955..9507eada3b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml @@ -65,7 +65,6 @@ - type: Appearance - type: StaticPrice price: 500 - - type: Execution - type: entity name: viper @@ -200,7 +199,7 @@ name: N1984 parent: BaseWeaponPistol id: WeaponPistolN1984 # the spaces in description are for formatting. - description: The sidearm of any self respecting officer. Comes in .45 magnum, the lord's caliber. + description: The sidearm of any self respecting officer. Comes in .45 magnum, the lord's caliber. components: - type: Sprite sprite: Objects/Weapons/Guns/Pistols/N1984.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml index 93e8b1a669..5bc8125eba 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml @@ -49,7 +49,6 @@ gun_chamber: !type:ContainerSlot - type: StaticPrice price: 500 - - type: Execution - type: entity name: AKMS diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml index ea82be46bc..b693bdba37 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml @@ -54,7 +54,6 @@ gun_chamber: !type:ContainerSlot - type: StaticPrice price: 500 - - type: Execution - type: entity name: Atreides diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml index 70ed96876e..f43df5f37c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml @@ -42,7 +42,6 @@ ents: [] - type: StaticPrice price: 500 - - type: Execution - type: entity name: Bulldog @@ -99,7 +98,6 @@ - type: Appearance - type: StaticPrice price: 500 - - type: Execution - type: entity name: double-barreled shotgun diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml index 6640e98f5f..753f109a17 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml @@ -36,7 +36,6 @@ ents: [] - type: StaticPrice price: 500 - - type: Execution - type: entity name: Kardashev-Mosin diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml index 2a07fd4fe0..cfcc0a01cd 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml @@ -36,4 +36,3 @@ quickEquip: false slots: - Belt - - type: Execution diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml index add776422d..ae1f5df3c1 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml @@ -107,7 +107,6 @@ containers: storagebase: !type:Container ents: [] - - type: Execution # shoots bullets instead of throwing them, no other changes - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml index 267e3a7891..497876f359 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml @@ -5,8 +5,6 @@ description: A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter. components: - type: Sharp - - type: Execution - doAfterDuration: 4.0 - type: Sprite sprite: Objects/Weapons/Melee/armblade.rsi state: icon diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml index aadb994244..b46ee09633 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml @@ -8,8 +8,6 @@ tags: - FireAxe - type: Sharp - - type: Execution - doAfterDuration: 4.0 - type: Sprite sprite: Objects/Weapons/Melee/fireaxe.rsi state: icon diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml index 432da5efe9..106e8bf440 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml @@ -7,8 +7,6 @@ tags: - Knife - type: Sharp - - type: Execution - doAfterDuration: 4.0 - type: Utensil types: - Knife diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index d27c6c6883..05cac3ae7b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -1,21 +1,10 @@ -- type: entity - name: Sword - parent: BaseItem - id: BaseSword - description: A sharp sword. - abstract: true - components: - - type: Sharp - - type: Execution - doAfterDuration: 4.0 - - type: DisarmMalus - - type: entity name: captain's sabre - parent: BaseSword + parent: BaseItem id: CaptainSabre description: A ceremonial weapon belonging to the captain of the station. components: + - type: Sharp - type: Sprite sprite: Objects/Weapons/Melee/captain_sabre.rsi state: icon @@ -37,13 +26,15 @@ - type: Tag tags: - CaptainSabre + - type: DisarmMalus - type: entity name: katana - parent: BaseSword + parent: BaseItem id: Katana description: Ancient craftwork made with not so ancient plasteel. components: + - type: Sharp - type: Tag tags: - Katana @@ -60,6 +51,7 @@ - type: Item size: Normal sprite: Objects/Weapons/Melee/katana.rsi + - type: DisarmMalus - type: entity name: energy katana @@ -94,10 +86,11 @@ - type: entity name: machete - parent: BaseSword + parent: BaseItem id: Machete description: A large, vicious looking blade. components: + - type: Sharp - type: Tag tags: - Machete @@ -114,13 +107,15 @@ - type: Item size: Normal sprite: Objects/Weapons/Melee/machete.rsi + - type: DisarmMalus - type: entity name: claymore - parent: BaseSword + parent: BaseItem id: Claymore description: An ancient war blade. components: + - type: Sharp - type: Sprite sprite: Objects/Weapons/Melee/claymore.rsi state: icon @@ -138,13 +133,15 @@ sprite: Objects/Weapons/Melee/claymore.rsi slots: - back + - type: DisarmMalus - type: entity name: cutlass - parent: BaseSword + parent: BaseItem id: Cutlass description: A wickedly curved blade, often seen in the hands of space pirates. components: + - type: Sharp - type: Tag tags: - Machete @@ -161,13 +158,15 @@ - type: Item size: Normal sprite: Objects/Weapons/Melee/cutlass.rsi + - type: DisarmMalus - type: entity name: The Throngler - parent: BaseSword + parent: BaseItem id: Throngler description: Why would you make this? components: + - type: Sharp - type: Sprite sprite: Objects/Weapons/Melee/Throngler2.rsi state: icon @@ -191,3 +190,4 @@ - type: Item size: Ginormous sprite: Objects/Weapons/Melee/Throngler-in-hand.rsi + - type: DisarmMalus