From 944c7dfb5d1a0db9efbdd13131313383fdfd674c Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 15:40:31 +1100 Subject: [PATCH 01/25] Execution (you monster) not done --- Content.Server/Execution/ExecutionSystem.cs | 107 ++++++++++++++++++ .../Locale/en-US/execution/execution.ftl | 10 ++ 2 files changed, 117 insertions(+) create mode 100644 Content.Server/Execution/ExecutionSystem.cs create mode 100644 Resources/Locale/en-US/execution/execution.ftl diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs new file mode 100644 index 000000000000..ea91691dbe47 --- /dev/null +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -0,0 +1,107 @@ +using Content.Server.Kitchen.Components; +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.Verbs; +using Content.Shared.Weapons.Melee; +using Content.Shared.Weapons.Ranged.Components; + +namespace Content.Server.Execution; + +/// +/// Verb for violently murdering cuffed creatures. +/// +public sealed class ExecutionSystem : EntitySystem +{ + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnGetInteractionVerbsMelee); + SubscribeLocalEvent>(OnGetInteractionVerbsGun); + } + + private void OnGetInteractionVerbsMelee( + EntityUid uid, + SharpComponent component, + GetVerbsEvent args) + { + if (args.Hands == null || args.Using == null || !args.CanAccess || !args.CanInteract) + return; + + UtilityVerb verb = new() + { + Act = () => + { + TryStartExecutionDoafterMelee(args.Using!.Value, args.Target, args.User); + }, + Impact = LogImpact.High, + Text = Loc.GetString("execution-verb-name"), + Message = Loc.GetString("execution-verb-message"), + }; + + args.Verbs.Add(verb); + } + + private void OnGetInteractionVerbsGun( + EntityUid uid, + GunComponent component, + GetVerbsEvent args) + { + if (args.Hands == null || args.Using == null || !args.CanAccess || !args.CanInteract) + return; + + UtilityVerb verb = new() + { + Act = () => + { + TryStartExecutionDoafterGun(args.Using!.Value, args.Target, args.User); + }, + Impact = LogImpact.High, + Text = Loc.GetString("execution-verb-name"), + Message = Loc.GetString("execution-verb-message"), + }; + + args.Verbs.Add(verb); + } + + private bool TryStartExecutionDoafterCommonChecks(EntityUid weapon, EntityUid victim, EntityUid user) + { + // No point executing someone if they can't take damage + if (!TryComp(victim, out var damage)) + return false; + + // You're not allowed to execute dead people (no fun allowed) + if (TryComp(victim, out var mobState) && !_mobStateSystem.IsDead(victim, mobState)) + return false; + + // All checks passed + return true; + } + + private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, EntityUid user) + { + if (!TryStartExecutionDoafterCommonChecks(weapon, victim, user)) + return; + + // We must be able to actually fire the gun and have it do damage + if (!TryComp(weapon, out var gun)) + return; + } + + private void TryStartExecutionDoafterGun(EntityUid weapon, EntityUid victim, EntityUid user) + { + if (!TryStartExecutionDoafterCommonChecks(weapon, victim, user)) + return; + + // We must be able to actually hurt people with the weapon + if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) + return; + } +} \ No newline at end of file diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl new file mode 100644 index 000000000000..771b7fba4e20 --- /dev/null +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -0,0 +1,10 @@ +execution-verb-name = Execute +execution-verb-message = Use your weapon to execute someone. + +execution-popup-gun-initial-internal = You ready the muzzle of {THE($weapon)} against {$target}'s head. +execution-popup-gun-initial-external = {$attacker} readies the muzzle of {THE($weapon)} against {$target}'s head. +execution-popup-gun-complete = {$attacker} blasts {$target} in the head! + +execution-popup-knife-initial-internal = You ready {THE($weapon)} against {$target}'s throat. +execution-popup-knife-initial-external = {$attacker} readies {POSS-ADJ($weapon)} against the throat of {$target}. +execution-popup-knife-complete = {$attacker} slits the throat of {$target}! \ No newline at end of file From ee97b9db561b41b4fade69cca94cf6c91e5713e6 Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 15:43:16 +1100 Subject: [PATCH 02/25] woops --- Content.Server/Execution/ExecutionSystem.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index ea91691dbe47..2ac02f3e56f9 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -90,8 +90,8 @@ private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, E if (!TryStartExecutionDoafterCommonChecks(weapon, victim, user)) return; - // We must be able to actually fire the gun and have it do damage - if (!TryComp(weapon, out var gun)) + // We must be able to actually hurt people with the weapon + if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) return; } @@ -100,8 +100,8 @@ private void TryStartExecutionDoafterGun(EntityUid weapon, EntityUid victim, Ent if (!TryStartExecutionDoafterCommonChecks(weapon, victim, user)) return; - // We must be able to actually hurt people with the weapon - if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) + // We must be able to actually fire the gun and have it do damage + if (!TryComp(weapon, out var gun)) return; } } \ No newline at end of file From eb458c8b2233b035ae3faa851fb7a76605c93771 Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 16:11:21 +1100 Subject: [PATCH 03/25] more stuff --- Content.Server/Execution/ExecutionSystem.cs | 76 ++++++++++++++++++- Content.Shared/Execution/DoafterEvent.cs | 9 +++ .../Locale/en-US/execution/execution.ftl | 10 +-- 3 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 Content.Shared/Execution/DoafterEvent.cs diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 2ac02f3e56f9..73f3536235a7 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -2,11 +2,14 @@ using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.DoAfter; +using Content.Shared.Execution; 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.Ranged.Components; +using Robust.Shared.Player; namespace Content.Server.Execution; @@ -16,8 +19,11 @@ namespace Content.Server.Execution; public sealed class ExecutionSystem : EntitySystem { [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + private const float ExecutionTime = 10.0f; + /// public override void Initialize() { @@ -25,6 +31,9 @@ public override void Initialize() SubscribeLocalEvent>(OnGetInteractionVerbsMelee); SubscribeLocalEvent>(OnGetInteractionVerbsGun); + + SubscribeLocalEvent(OnDoafterMelee); + SubscribeLocalEvent(OnDoafterGun); } private void OnGetInteractionVerbsMelee( @@ -71,14 +80,14 @@ private void OnGetInteractionVerbsGun( args.Verbs.Add(verb); } - private bool TryStartExecutionDoafterCommonChecks(EntityUid weapon, EntityUid victim, EntityUid user) + private bool TryStartExecutionDoafterChecks(EntityUid weapon, EntityUid victim, EntityUid user) { // No point executing someone if they can't take damage if (!TryComp(victim, out var damage)) return false; // You're not allowed to execute dead people (no fun allowed) - if (TryComp(victim, out var mobState) && !_mobStateSystem.IsDead(victim, mobState)) + if (TryComp(victim, out var mobState) && _mobStateSystem.IsDead(victim, mobState)) return false; // All checks passed @@ -87,21 +96,80 @@ private bool TryStartExecutionDoafterCommonChecks(EntityUid weapon, EntityUid vi private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, EntityUid user) { - if (!TryStartExecutionDoafterCommonChecks(weapon, victim, user)) + if (!TryStartExecutionDoafterChecks(weapon, victim, user)) return; // We must be able to actually hurt people with the weapon if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) return; + + _popupSystem.PopupEntity(Loc.GetString( + "execution-popup-melee-initial-internal", ("weapon", weapon), ("victim", victim)), + user, Filter.Entities(user), true, PopupType.Medium); + _popupSystem.PopupEntity(Loc.GetString( + "execution-popup-melee-initial-external", ("weapon", weapon), ("victim", victim)), + user, Filter.PvsExcept(user), true, PopupType.MediumCaution); + + var doAfter = + new DoAfterArgs(EntityManager, user, ExecutionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) + { + BreakOnTargetMove = true, + BreakOnUserMove = true, + BreakOnDamage = true, + NeedHand = true + }; + + _doAfterSystem.TryStartDoAfter(doAfter); } private void TryStartExecutionDoafterGun(EntityUid weapon, EntityUid victim, EntityUid user) { - if (!TryStartExecutionDoafterCommonChecks(weapon, victim, user)) + if (!TryStartExecutionDoafterChecks(weapon, victim, user)) return; // We must be able to actually fire the gun and have it do damage if (!TryComp(weapon, out var gun)) return; + + _popupSystem.PopupEntity(Loc.GetString( + "execution-popup-gun-initial-internal", ("weapon", weapon), ("victim", victim)), + user, Filter.Entities(user), true, PopupType.Medium); + _popupSystem.PopupEntity(Loc.GetString( + "execution-popup-gun-initial-external", ("weapon", weapon), ("victim", victim)), + user, Filter.PvsExcept(user), true, PopupType.MediumCaution); + + var doAfter = + new DoAfterArgs(EntityManager, user, ExecutionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) + { + BreakOnTargetMove = true, + BreakOnUserMove = true, + BreakOnDamage = true, + NeedHand = true + }; + + _doAfterSystem.TryStartDoAfter(doAfter); + } + + private bool OnDoafterChecks(EntityUid uid, DoAfterEvent args) + { + if (args.Handled || args.Cancelled) + return false; + + // All checks passed + return true; + } + + private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEvent args) + { + if (!OnDoafterChecks(uid, args)) + return; + + TryComp(args.Target!.Value, out var damage); + } + + private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent args) + { + if (!OnDoafterChecks(uid, args)) + return; } } \ No newline at end of file diff --git a/Content.Shared/Execution/DoafterEvent.cs b/Content.Shared/Execution/DoafterEvent.cs new file mode 100644 index 000000000000..785497452763 --- /dev/null +++ b/Content.Shared/Execution/DoafterEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.Execution; + +[Serializable, NetSerializable] +public sealed partial class ExecutionDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl index 771b7fba4e20..25fb29089a40 100644 --- a/Resources/Locale/en-US/execution/execution.ftl +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -1,10 +1,10 @@ execution-verb-name = Execute execution-verb-message = Use your weapon to execute someone. -execution-popup-gun-initial-internal = You ready the muzzle of {THE($weapon)} against {$target}'s head. -execution-popup-gun-initial-external = {$attacker} readies the muzzle of {THE($weapon)} against {$target}'s head. +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 = {$attacker} blasts {$target} in the head! -execution-popup-knife-initial-internal = You ready {THE($weapon)} against {$target}'s throat. -execution-popup-knife-initial-external = {$attacker} readies {POSS-ADJ($weapon)} against the throat of {$target}. -execution-popup-knife-complete = {$attacker} slits the throat of {$target}! \ No newline at end of file +execution-popup-melee-initial-internal = You ready {THE($weapon)} against {$victim}'s throat. +execution-popup-melee-initial-external = {$attacker} readies {POSS-ADJ($weapon)} against the throat of {$victim}. +execution-popup-melee-complete = {$attacker} slits the throat of {$target}! \ No newline at end of file From 8d9adc6dbfd3a27139665b5649798b750067e3a3 Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 17:07:22 +1100 Subject: [PATCH 04/25] Melee executions --- Content.Server/Execution/ExecutionSystem.cs | 40 ++++++++++++++++--- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 2 +- .../Locale/en-US/execution/execution.ftl | 6 ++- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 73f3536235a7..92cbd7189a5f 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -1,4 +1,6 @@ +using Content.Server.Cuffs; using Content.Server.Kitchen.Components; +using Content.Shared.Cuffs.Components; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.DoAfter; @@ -21,8 +23,11 @@ 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 DamageableSystem _damageableSystem = default!; + [Dependency] private readonly SharedMeleeWeaponSystem _meleeSystem = default!; private const float ExecutionTime = 10.0f; + private const float DamageModifier = 9.0f; /// public override void Initialize() @@ -80,14 +85,18 @@ private void OnGetInteractionVerbsGun( args.Verbs.Add(verb); } - private bool TryStartExecutionDoafterChecks(EntityUid weapon, EntityUid victim, EntityUid user) + private bool CanExecuteChecks(EntityUid weapon, EntityUid victim, EntityUid user) { // No point executing someone if they can't take damage if (!TryComp(victim, out var damage)) return false; // You're not allowed to execute dead people (no fun allowed) - if (TryComp(victim, out var mobState) && _mobStateSystem.IsDead(victim, mobState)) + if (TryComp(victim, out var mobState) && !_mobStateSystem.IsAlive(victim, mobState)) + return false; + + // They have to be cuffed to be executed + if (!TryComp(victim, out var cuffable) || cuffable.CanStillInteract) return false; // All checks passed @@ -96,7 +105,7 @@ private bool TryStartExecutionDoafterChecks(EntityUid weapon, EntityUid victim, private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, EntityUid user) { - if (!TryStartExecutionDoafterChecks(weapon, victim, user)) + if (!CanExecuteChecks(weapon, victim, user)) return; // We must be able to actually hurt people with the weapon @@ -124,7 +133,7 @@ private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, E private void TryStartExecutionDoafterGun(EntityUid weapon, EntityUid victim, EntityUid user) { - if (!TryStartExecutionDoafterChecks(weapon, victim, user)) + if (!CanExecuteChecks(weapon, victim, user)) return; // We must be able to actually fire the gun and have it do damage @@ -152,7 +161,10 @@ private void TryStartExecutionDoafterGun(EntityUid weapon, EntityUid victim, Ent private bool OnDoafterChecks(EntityUid uid, DoAfterEvent args) { - if (args.Handled || args.Cancelled) + if (args.Handled || args.Cancelled || args.Used == null || args.Target == null) + return false; + + if (!CanExecuteChecks(args.Used.Value, args.Target.Value, uid)) return false; // All checks passed @@ -164,7 +176,23 @@ private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEven if (!OnDoafterChecks(uid, args)) return; - TryComp(args.Target!.Value, out var damage); + // These are fine because it's checked in OnDoafterChecks + var attacker = args.User; + var weapon = args.Used!.Value; + var victim = args.Target!.Value; + + if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) + return; + + _damageableSystem.TryChangeDamage(victim, melee.Damage * DamageModifier); + _meleeSystem.PlayHitSound(victim, weapon, null, null, null); + + _popupSystem.PopupEntity(Loc.GetString( + "execution-popup-melee-complete-internal", ("victim", victim)), + attacker, Filter.Entities(attacker), true, PopupType.Medium); + _popupSystem.PopupEntity(Loc.GetString( + "execution-popup-melee-complete-external", ("attacker", weapon), ("victim", victim)), + attacker, Filter.PvsExcept(attacker), true, PopupType.MediumCaution); } private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent args) diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index c6adf0011324..6fa461860f6e 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -736,7 +736,7 @@ protected virtual bool ArcRaySuccessful(EntityUid targetUid, Vector2 position, A return true; } - private void PlayHitSound(EntityUid target, EntityUid? user, string? type, SoundSpecifier? hitSoundOverride, SoundSpecifier? hitSound) + public void PlayHitSound(EntityUid target, EntityUid? user, string? type, SoundSpecifier? hitSoundOverride, SoundSpecifier? hitSound) { var playedSound = false; diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl index 25fb29089a40..2ba86a66fb63 100644 --- a/Resources/Locale/en-US/execution/execution.ftl +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -3,8 +3,10 @@ execution-verb-message = Use your weapon to execute someone. 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 = {$attacker} blasts {$target} in the 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-melee-initial-internal = You ready {THE($weapon)} against {$victim}'s throat. execution-popup-melee-initial-external = {$attacker} readies {POSS-ADJ($weapon)} against the throat of {$victim}. -execution-popup-melee-complete = {$attacker} slits the throat of {$target}! \ No newline at end of file +execution-popup-melee-complete-internal = You slit the throat of {$victim}! +execution-popup-melee-complete-external = {$attacker} slits the throat of {$victim}! \ No newline at end of file From d5589ac5266f12a6fbf6810c88fc7c412ccfbafa Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 17:20:07 +1100 Subject: [PATCH 05/25] Prevent executing those who can interact --- Content.Server/Execution/ExecutionSystem.cs | 32 +++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 92cbd7189a5f..d2e5f2f01e63 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -1,6 +1,5 @@ -using Content.Server.Cuffs; using Content.Server.Kitchen.Components; -using Content.Shared.Cuffs.Components; +using Content.Shared.ActionBlocker; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.DoAfter; @@ -23,6 +22,7 @@ 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 DamageableSystem _damageableSystem = default!; [Dependency] private readonly SharedMeleeWeaponSystem _meleeSystem = default!; @@ -49,11 +49,18 @@ private void OnGetInteractionVerbsMelee( 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 (!CanExecuteChecks(weapon, victim, victim)) + return; + UtilityVerb verb = new() { Act = () => { - TryStartExecutionDoafterMelee(args.Using!.Value, args.Target, args.User); + TryStartExecutionDoafterMelee(weapon, victim, attacker); }, Impact = LogImpact.High, Text = Loc.GetString("execution-verb-name"), @@ -71,11 +78,18 @@ private void OnGetInteractionVerbsGun( 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 (!CanExecuteChecks(weapon, victim, victim)) + return; + UtilityVerb verb = new() { Act = () => { - TryStartExecutionDoafterGun(args.Using!.Value, args.Target, args.User); + TryStartExecutionDoafterGun(weapon, victim, attacker); }, Impact = LogImpact.High, Text = Loc.GetString("execution-verb-name"), @@ -92,11 +106,11 @@ private bool CanExecuteChecks(EntityUid weapon, EntityUid victim, EntityUid user return false; // You're not allowed to execute dead people (no fun allowed) - if (TryComp(victim, out var mobState) && !_mobStateSystem.IsAlive(victim, mobState)) + if (TryComp(victim, out var mobState) && _mobStateSystem.IsDead(victim, mobState)) return false; - - // They have to be cuffed to be executed - if (!TryComp(victim, out var cuffable) || cuffable.CanStillInteract) + + // You must be incapacitated to be executed + if (_actionBlockerSystem.CanInteract(victim, null)) return false; // All checks passed @@ -184,7 +198,7 @@ private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEven if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) return; - _damageableSystem.TryChangeDamage(victim, melee.Damage * DamageModifier); + _damageableSystem.TryChangeDamage(victim, melee.Damage * DamageModifier, true); _meleeSystem.PlayHitSound(victim, weapon, null, null, null); _popupSystem.PopupEntity(Loc.GetString( From 0f8cda9a0fb6942fe4338a081c8be0987b525cc5 Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 17:29:03 +1100 Subject: [PATCH 06/25] Better checks for if you can execute --- Content.Server/Execution/ExecutionSystem.cs | 57 ++++++++++++++------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index d2e5f2f01e63..425b6b30e878 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -53,7 +53,7 @@ private void OnGetInteractionVerbsMelee( var weapon = args.Using!.Value; var victim = args.Target; - if (!CanExecuteChecks(weapon, victim, victim)) + if (!CanExecuteChecksMelee(weapon, victim, victim)) return; UtilityVerb verb = new() @@ -82,7 +82,7 @@ private void OnGetInteractionVerbsGun( var weapon = args.Using!.Value; var victim = args.Target; - if (!CanExecuteChecks(weapon, victim, victim)) + if (!CanExecuteChecksGun(weapon, victim, victim)) return; UtilityVerb verb = new() @@ -116,16 +116,34 @@ private bool CanExecuteChecks(EntityUid weapon, EntityUid victim, EntityUid user // All checks passed return true; } - - private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, EntityUid user) + + private bool CanExecuteChecksMelee(EntityUid weapon, EntityUid victim, EntityUid user) { - if (!CanExecuteChecks(weapon, victim, user)) - return; + if (!CanExecuteChecks(weapon, victim, user)) return false; // We must be able to actually hurt people with the weapon if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) - return; + return false; + + return true; + } + + private bool CanExecuteChecksGun(EntityUid weapon, EntityUid victim, EntityUid user) + { + if (!CanExecuteChecks(weapon, victim, user)) return false; + // We must be able to actually fire the gun and have it do damage + if (!TryComp(weapon, out var gun)) + return false; + + return true; + } + + private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, EntityUid user) + { + if (!CanExecuteChecksMelee(weapon, victim, user)) + return; + _popupSystem.PopupEntity(Loc.GetString( "execution-popup-melee-initial-internal", ("weapon", weapon), ("victim", victim)), user, Filter.Entities(user), true, PopupType.Medium); @@ -147,13 +165,9 @@ private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, E private void TryStartExecutionDoafterGun(EntityUid weapon, EntityUid victim, EntityUid user) { - if (!CanExecuteChecks(weapon, victim, user)) - return; - - // We must be able to actually fire the gun and have it do damage - if (!TryComp(weapon, out var gun)) + if (!CanExecuteChecksGun(weapon, victim, user)) return; - + _popupSystem.PopupEntity(Loc.GetString( "execution-popup-gun-initial-internal", ("weapon", weapon), ("victim", victim)), user, Filter.Entities(user), true, PopupType.Medium); @@ -187,14 +201,15 @@ private bool OnDoafterChecks(EntityUid uid, DoAfterEvent args) private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEvent args) { - if (!OnDoafterChecks(uid, args)) + if (args.Handled || args.Cancelled || args.Used == null || args.Target == null) return; - - // These are fine because it's checked in OnDoafterChecks + var attacker = args.User; var weapon = args.Used!.Value; var victim = args.Target!.Value; + if (!CanExecuteChecksMelee(weapon, victim, attacker)) return; + if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) return; @@ -205,13 +220,19 @@ private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEven "execution-popup-melee-complete-internal", ("victim", victim)), attacker, Filter.Entities(attacker), true, PopupType.Medium); _popupSystem.PopupEntity(Loc.GetString( - "execution-popup-melee-complete-external", ("attacker", weapon), ("victim", victim)), + "execution-popup-melee-complete-external", ("attacker", attacker), ("victim", victim)), attacker, Filter.PvsExcept(attacker), true, PopupType.MediumCaution); } private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent args) { - if (!OnDoafterChecks(uid, args)) + if (args.Handled || args.Cancelled || args.Used == null || args.Target == null) return; + + var attacker = args.User; + var weapon = args.Used!.Value; + var victim = args.Target!.Value; + + if (!CanExecuteChecksGun(weapon, victim, attacker)) return; } } \ No newline at end of file From 2f3fd4e4eea92fffc0aef6459f63a9585c94ced0 Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 17:32:43 +1100 Subject: [PATCH 07/25] Scale the execution time of a knife with its attack speed --- Content.Server/Execution/ExecutionSystem.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 425b6b30e878..a39a0642ded6 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -26,7 +26,8 @@ public sealed class ExecutionSystem : EntitySystem [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly SharedMeleeWeaponSystem _meleeSystem = default!; - private const float ExecutionTime = 10.0f; + private const float MeleeExecutionTimeModifier = 5.0f; + private const float GunExecutionTime = 10.0f; private const float DamageModifier = 9.0f; /// @@ -144,6 +145,8 @@ private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, E if (!CanExecuteChecksMelee(weapon, victim, user)) return; + var executionTime = Comp(weapon).AttackRate * MeleeExecutionTimeModifier; + _popupSystem.PopupEntity(Loc.GetString( "execution-popup-melee-initial-internal", ("weapon", weapon), ("victim", victim)), user, Filter.Entities(user), true, PopupType.Medium); @@ -152,7 +155,7 @@ private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, E user, Filter.PvsExcept(user), true, PopupType.MediumCaution); var doAfter = - new DoAfterArgs(EntityManager, user, ExecutionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) + new DoAfterArgs(EntityManager, user, executionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) { BreakOnTargetMove = true, BreakOnUserMove = true, @@ -176,7 +179,7 @@ private void TryStartExecutionDoafterGun(EntityUid weapon, EntityUid victim, Ent user, Filter.PvsExcept(user), true, PopupType.MediumCaution); var doAfter = - new DoAfterArgs(EntityManager, user, ExecutionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) + new DoAfterArgs(EntityManager, user, GunExecutionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) { BreakOnTargetMove = true, BreakOnUserMove = true, From f409622eba6d94661a62bd2bdfa0ae311eb2fc00 Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 18:02:14 +1100 Subject: [PATCH 08/25] Translations for fucking up an execution --- Resources/Locale/en-US/execution/execution.ftl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl index 2ba86a66fb63..d89b1fb6cce9 100644 --- a/Resources/Locale/en-US/execution/execution.ftl +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -5,8 +5,10 @@ execution-popup-gun-initial-internal = You ready the muzzle of {THE($weapon)} ag 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-melee-initial-internal = You ready {THE($weapon)} against {$victim}'s throat. -execution-popup-melee-initial-external = {$attacker} readies {POSS-ADJ($weapon)} against the throat of {$victim}. +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}! \ No newline at end of file From ed4e4a2b14d5e8f3470dd4817c8803b4df4581ce Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 18:19:02 +1100 Subject: [PATCH 09/25] rename some functions --- Content.Server/Execution/ExecutionSystem.cs | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index a39a0642ded6..de97dc538770 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -54,14 +54,14 @@ private void OnGetInteractionVerbsMelee( var weapon = args.Using!.Value; var victim = args.Target; - if (!CanExecuteChecksMelee(weapon, victim, victim)) + if (!CanExecuteWithMelee(weapon, victim, victim)) return; UtilityVerb verb = new() { Act = () => { - TryStartExecutionDoafterMelee(weapon, victim, attacker); + TryStartMeleeExecutionDoafter(weapon, victim, attacker); }, Impact = LogImpact.High, Text = Loc.GetString("execution-verb-name"), @@ -83,14 +83,14 @@ private void OnGetInteractionVerbsGun( var weapon = args.Using!.Value; var victim = args.Target; - if (!CanExecuteChecksGun(weapon, victim, victim)) + if (!CanExecuteWithGun(weapon, victim, victim)) return; UtilityVerb verb = new() { Act = () => { - TryStartExecutionDoafterGun(weapon, victim, attacker); + TryStartGunExecutionDoafter(weapon, victim, attacker); }, Impact = LogImpact.High, Text = Loc.GetString("execution-verb-name"), @@ -100,7 +100,7 @@ private void OnGetInteractionVerbsGun( args.Verbs.Add(verb); } - private bool CanExecuteChecks(EntityUid weapon, EntityUid victim, EntityUid user) + private bool CanExecuteWithAny(EntityUid weapon, EntityUid victim, EntityUid user) { // No point executing someone if they can't take damage if (!TryComp(victim, out var damage)) @@ -118,9 +118,9 @@ private bool CanExecuteChecks(EntityUid weapon, EntityUid victim, EntityUid user return true; } - private bool CanExecuteChecksMelee(EntityUid weapon, EntityUid victim, EntityUid user) + private bool CanExecuteWithMelee(EntityUid weapon, EntityUid victim, EntityUid user) { - if (!CanExecuteChecks(weapon, victim, user)) return false; + if (!CanExecuteWithAny(weapon, victim, user)) return false; // We must be able to actually hurt people with the weapon if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) @@ -129,9 +129,9 @@ private bool CanExecuteChecksMelee(EntityUid weapon, EntityUid victim, EntityUid return true; } - private bool CanExecuteChecksGun(EntityUid weapon, EntityUid victim, EntityUid user) + private bool CanExecuteWithGun(EntityUid weapon, EntityUid victim, EntityUid user) { - if (!CanExecuteChecks(weapon, victim, user)) return false; + if (!CanExecuteWithAny(weapon, victim, user)) return false; // We must be able to actually fire the gun and have it do damage if (!TryComp(weapon, out var gun)) @@ -140,9 +140,9 @@ private bool CanExecuteChecksGun(EntityUid weapon, EntityUid victim, EntityUid u return true; } - private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, EntityUid user) + private void TryStartMeleeExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid user) { - if (!CanExecuteChecksMelee(weapon, victim, user)) + if (!CanExecuteWithMelee(weapon, victim, user)) return; var executionTime = Comp(weapon).AttackRate * MeleeExecutionTimeModifier; @@ -166,9 +166,9 @@ private void TryStartExecutionDoafterMelee(EntityUid weapon, EntityUid victim, E _doAfterSystem.TryStartDoAfter(doAfter); } - private void TryStartExecutionDoafterGun(EntityUid weapon, EntityUid victim, EntityUid user) + private void TryStartGunExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid user) { - if (!CanExecuteChecksGun(weapon, victim, user)) + if (!CanExecuteWithGun(weapon, victim, user)) return; _popupSystem.PopupEntity(Loc.GetString( @@ -195,7 +195,7 @@ private bool OnDoafterChecks(EntityUid uid, DoAfterEvent args) if (args.Handled || args.Cancelled || args.Used == null || args.Target == null) return false; - if (!CanExecuteChecks(args.Used.Value, args.Target.Value, uid)) + if (!CanExecuteWithAny(args.Used.Value, args.Target.Value, uid)) return false; // All checks passed @@ -211,7 +211,7 @@ private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEven var weapon = args.Used!.Value; var victim = args.Target!.Value; - if (!CanExecuteChecksMelee(weapon, victim, attacker)) return; + if (!CanExecuteWithMelee(weapon, victim, attacker)) return; if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) return; @@ -236,6 +236,6 @@ private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent ar var weapon = args.Used!.Value; var victim = args.Target!.Value; - if (!CanExecuteChecksGun(weapon, victim, attacker)) return; + if (!CanExecuteWithGun(weapon, victim, attacker)) return; } } \ No newline at end of file From 3d94183aef8200cdd0d244a4e4f08dbf65686ff3 Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 20:17:44 +1100 Subject: [PATCH 10/25] Properly scale execution speed of melee weapons --- Content.Server/Execution/ExecutionSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index de97dc538770..61c22c75b8dc 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -145,7 +145,7 @@ private void TryStartMeleeExecutionDoafter(EntityUid weapon, EntityUid victim, E if (!CanExecuteWithMelee(weapon, victim, user)) return; - var executionTime = Comp(weapon).AttackRate * MeleeExecutionTimeModifier; + var executionTime = (1.0f / Comp(weapon).AttackRate) * MeleeExecutionTimeModifier; _popupSystem.PopupEntity(Loc.GetString( "execution-popup-melee-initial-internal", ("weapon", weapon), ("victim", victim)), From 33767cb26ab3a0e3d9aab6d85fe45bb140d2bc65 Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 21:02:22 +1100 Subject: [PATCH 11/25] Fix checks in CanExecuteWithAny --- Content.Server/Execution/ExecutionSystem.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 61c22c75b8dc..4605ee5132f2 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -106,8 +106,12 @@ private bool CanExecuteWithAny(EntityUid weapon, EntityUid victim, EntityUid use if (!TryComp(victim, out var damage)) 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 (TryComp(victim, out var mobState) && _mobStateSystem.IsDead(victim, mobState)) + if (_mobStateSystem.IsDead(victim, mobState)) return false; // You must be incapacitated to be executed From 920c66f1e386490a04fbd32b19ff98e1bec9f295 Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 21:07:09 +1100 Subject: [PATCH 12/25] Allow executing yourself (funny) --- Content.Server/Execution/ExecutionSystem.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 4605ee5132f2..efd78ad218e9 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -114,8 +114,12 @@ private bool CanExecuteWithAny(EntityUid weapon, EntityUid victim, EntityUid use if (_mobStateSystem.IsDead(victim, mobState)) return false; - // You must be incapacitated to be executed - if (_actionBlockerSystem.CanInteract(victim, null)) + // You must be incapacitated to execute someone else + if (victim != user && _actionBlockerSystem.CanInteract(victim, null)) + return false; + + // You must be not incapacitated to execute yourself + if (victim == user && !_actionBlockerSystem.CanInteract(victim, null)) return false; // All checks passed From 645d091f3c1d66171ca5766fabca0f302e4f11e7 Mon Sep 17 00:00:00 2001 From: veritius Date: Tue, 16 Jan 2024 21:52:23 +1100 Subject: [PATCH 13/25] More versatile localisation --- Content.Server/Execution/ExecutionSystem.cs | 74 ++++++++++++------- .../Locale/en-US/execution/execution.ftl | 17 ++++- 2 files changed, 63 insertions(+), 28 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index efd78ad218e9..50317174fd31 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -148,22 +148,26 @@ private bool CanExecuteWithGun(EntityUid weapon, EntityUid victim, EntityUid use return true; } - private void TryStartMeleeExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid user) + private void TryStartMeleeExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid attacker) { - if (!CanExecuteWithMelee(weapon, victim, user)) + if (!CanExecuteWithMelee(weapon, victim, attacker)) return; var executionTime = (1.0f / Comp(weapon).AttackRate) * MeleeExecutionTimeModifier; - _popupSystem.PopupEntity(Loc.GetString( - "execution-popup-melee-initial-internal", ("weapon", weapon), ("victim", victim)), - user, Filter.Entities(user), true, PopupType.Medium); - _popupSystem.PopupEntity(Loc.GetString( - "execution-popup-melee-initial-external", ("weapon", weapon), ("victim", victim)), - user, Filter.PvsExcept(user), true, PopupType.MediumCaution); + if (attacker == victim) + { + ShowExecutionPopup("suicide-popup-melee-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("suicide-popup-melee-initial-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + } + else + { + ShowExecutionPopup("execution-popup-melee-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("execution-popup-melee-initial-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + } var doAfter = - new DoAfterArgs(EntityManager, user, executionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) + new DoAfterArgs(EntityManager, attacker, executionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) { BreakOnTargetMove = true, BreakOnUserMove = true, @@ -174,20 +178,24 @@ private void TryStartMeleeExecutionDoafter(EntityUid weapon, EntityUid victim, E _doAfterSystem.TryStartDoAfter(doAfter); } - private void TryStartGunExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid user) + private void TryStartGunExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid attacker) { - if (!CanExecuteWithGun(weapon, victim, user)) + if (!CanExecuteWithGun(weapon, victim, attacker)) return; - - _popupSystem.PopupEntity(Loc.GetString( - "execution-popup-gun-initial-internal", ("weapon", weapon), ("victim", victim)), - user, Filter.Entities(user), true, PopupType.Medium); - _popupSystem.PopupEntity(Loc.GetString( - "execution-popup-gun-initial-external", ("weapon", weapon), ("victim", victim)), - user, Filter.PvsExcept(user), true, PopupType.MediumCaution); + if (attacker == victim) + { + ShowExecutionPopup("suicide-popup-gun-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("suicide-popup-gun-initial-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + } + else + { + ShowExecutionPopup("execution-popup-gun-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("execution-popup-gun-initial-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + } + var doAfter = - new DoAfterArgs(EntityManager, user, GunExecutionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) + new DoAfterArgs(EntityManager, attacker, GunExecutionTime, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon) { BreakOnTargetMove = true, BreakOnUserMove = true, @@ -216,8 +224,8 @@ private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEven return; var attacker = args.User; - var weapon = args.Used!.Value; var victim = args.Target!.Value; + var weapon = args.Used!.Value; if (!CanExecuteWithMelee(weapon, victim, attacker)) return; @@ -226,13 +234,17 @@ private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEven _damageableSystem.TryChangeDamage(victim, melee.Damage * DamageModifier, true); _meleeSystem.PlayHitSound(victim, weapon, null, null, null); - - _popupSystem.PopupEntity(Loc.GetString( - "execution-popup-melee-complete-internal", ("victim", victim)), - attacker, Filter.Entities(attacker), true, PopupType.Medium); - _popupSystem.PopupEntity(Loc.GetString( - "execution-popup-melee-complete-external", ("attacker", attacker), ("victim", victim)), - attacker, Filter.PvsExcept(attacker), true, PopupType.MediumCaution); + + if (attacker == victim) + { + ShowExecutionPopup("suicide-popup-melee-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("suicide-popup-melee-complete-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + } + else + { + ShowExecutionPopup("execution-popup-melee-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("execution-popup-melee-complete-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + } } private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent args) @@ -246,4 +258,12 @@ private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent ar if (!CanExecuteWithGun(weapon, victim, attacker)) return; } + + private void ShowExecutionPopup(string locString, Filter filter, PopupType type, + EntityUid attacker, EntityUid victim, EntityUid weapon) + { + _popupSystem.PopupEntity(Loc.GetString( + locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), + attacker, filter, true, type); + } } \ No newline at end of file diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl index d89b1fb6cce9..d8a3e5d56f43 100644 --- a/Resources/Locale/en-US/execution/execution.ftl +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -1,6 +1,11 @@ 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! @@ -8,7 +13,17 @@ 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! +suicide-popup-gun-initial-internal = You place the muzzle of {THE($weapon)} in your mouth. +suicide-popup-gun-initial-external = {$attacker} places the muzzle of {THE($weapon)} in {POSS-ADJ($attacker)} mouth. +suicide-popup-gun-complete-internal = You shoot yourself in the head! +suicide-popup-gun-complete-external = {$attacker} shoots {REFLEXIVE($attacker)} in the head! + 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}! \ No newline at end of file +execution-popup-melee-complete-external = {$attacker} slits the throat of {$victim}! + +suicide-popup-melee-initial-internal = You ready {THE($weapon)} against your throat. +suicide-popup-melee-initial-external = {$attacker} readies {POSS-ADJ(attacker)} {$weapon} against their throat. +suicide-popup-melee-complete-internal = You slit your throat with {THE($weapon)}. +suicide-popup-melee-complete-external = {$attacker} slits their throat with {THE($weapon)}. \ No newline at end of file From f6ade52f9731fa2c28033dd44fa6dc2b0f57a537 Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 00:09:32 +1100 Subject: [PATCH 14/25] Suicide with guns --- Content.Server/Execution/ExecutionSystem.cs | 122 +++++++++++++++++- .../Locale/en-US/execution/execution.ftl | 4 +- 2 files changed, 117 insertions(+), 9 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 50317174fd31..8b863b453e20 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -1,16 +1,28 @@ +using System.Numerics; +using Content.Server.Interaction; using Content.Server.Kitchen.Components; +using Content.Server.Weapons.Ranged.Systems; using Content.Shared.ActionBlocker; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Execution; +using Content.Shared.Interaction.Components; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; +using Content.Shared.Projectiles; using Content.Shared.Verbs; using Content.Shared.Weapons.Melee; +using Content.Shared.Weapons.Ranged; using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Map; using Robust.Shared.Player; +using Robust.Shared.Prototypes; namespace Content.Server.Execution; @@ -22,9 +34,14 @@ 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 InteractionSystem _interactionSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly SharedMeleeWeaponSystem _meleeSystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IComponentFactory _componentFactory = default!; + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; private const float MeleeExecutionTimeModifier = 5.0f; private const float GunExecutionTime = 10.0f; @@ -114,7 +131,7 @@ private bool CanExecuteWithAny(EntityUid weapon, EntityUid victim, EntityUid use if (_mobStateSystem.IsDead(victim, mobState)) return false; - // You must be incapacitated to execute someone else + // The victim must be incapacitated to be executed if (victim != user && _actionBlockerSystem.CanInteract(victim, null)) return false; @@ -158,12 +175,12 @@ private void TryStartMeleeExecutionDoafter(EntityUid weapon, EntityUid victim, E if (attacker == victim) { ShowExecutionPopup("suicide-popup-melee-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("suicide-popup-melee-initial-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("suicide-popup-melee-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); } else { ShowExecutionPopup("execution-popup-melee-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("execution-popup-melee-initial-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("execution-popup-melee-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); } var doAfter = @@ -186,12 +203,12 @@ private void TryStartGunExecutionDoafter(EntityUid weapon, EntityUid victim, Ent if (attacker == victim) { ShowExecutionPopup("suicide-popup-gun-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("suicide-popup-gun-initial-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("suicide-popup-gun-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); } else { ShowExecutionPopup("execution-popup-gun-initial-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("execution-popup-gun-initial-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("execution-popup-gun-initial-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); } var doAfter = @@ -238,15 +255,16 @@ private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEven if (attacker == victim) { ShowExecutionPopup("suicide-popup-melee-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("suicide-popup-melee-complete-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("suicide-popup-melee-complete-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); } else { ShowExecutionPopup("execution-popup-melee-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("execution-popup-melee-complete-external", Filter.PvsExcept(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("execution-popup-melee-complete-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); } } + // TODO: This repeats a lot of the code of the serverside GunSystem, make it not do that private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent args) { if (args.Handled || args.Cancelled || args.Used == null || args.Target == null) @@ -257,6 +275,96 @@ private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent ar var victim = args.Target!.Value; if (!CanExecuteWithGun(weapon, victim, attacker)) return; + + // Check if any systems want to block our shot + var prevention = new ShotAttemptedEvent + { + User = attacker, + Used = weapon + }; + + RaiseLocalEvent(weapon, ref prevention); + if (prevention.Cancelled) + return; + + RaiseLocalEvent(attacker, ref prevention); + if (prevention.Cancelled) + return; + + // Not sure what this is for but gunsystem uses it so ehhh + var attemptEv = new AttemptShootEvent(attacker, null); + RaiseLocalEvent(weapon, ref attemptEv); + + if (attemptEv.Cancelled) + { + if (attemptEv.Message != null) + { + _popupSystem.PopupClient(attemptEv.Message, weapon, attacker); + return; + } + } + + // Take some ammunition for the shot (one bullet) + var fromCoordinates = Transform(attacker).Coordinates; + var ev = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, attacker); + RaiseLocalEvent(weapon, ev); + + // Check if there's any ammo left + if (ev.Ammo.Count <= 0) return; + + // Information about the ammo like damage + SoundSpecifier? shootSound = component.SoundGunshot; + DamageSpecifier? damage = null; + + // Get some information from IShootable + var ammo_uid = ev.Ammo[0].Entity; + switch (ev.Ammo[0].Shootable) + { + case CartridgeAmmoComponent cartridge: + // Get the damage value + var prototype = _prototypeManager.Index(cartridge.Prototype); + prototype.TryGetComponent(out var projectileA, _componentFactory); // sloth forgive me + damage = projectileA!.Damage * cartridge.Count; + + // Expend the cartridge + cartridge.Spent = true; + _appearanceSystem.SetData(ammo_uid!.Value, AmmoVisuals.Spent, true); + Dirty(ammo_uid.Value, cartridge); + + break; + + case AmmoComponent newAmmo: + TryComp(ammo_uid, out var projectileB); + damage = projectileB!.Damage; + Del(ammo_uid); + break; + + case HitscanPrototype hitscan: + damage = hitscan.Damage!; + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + // Check for clumsiness + if (TryComp(attacker, out var clumsy) && component.ClumsyProof == false) + { + if (_interactionSystem.TryRollClumsy(attacker, 0.33333333f, clumsy)) + { + _damageableSystem.TryChangeDamage(attacker, damage, origin: attacker); + + ShowExecutionPopup("execution-popup-gun-clumsy-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("execution-popup-gun-clumsy-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); + + _audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, false, AudioParams.Default); + return; + } + } + + // Gun successfully fired, deal damage + _damageableSystem.TryChangeDamage(victim, damage * DamageModifier, true); + _audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, false, AudioParams.Default); } private void ShowExecutionPopup(string locString, Filter filter, PopupType type, diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl index d8a3e5d56f43..8a013ea9fa55 100644 --- a/Resources/Locale/en-US/execution/execution.ftl +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -19,11 +19,11 @@ suicide-popup-gun-complete-internal = You shoot yourself in the head! suicide-popup-gun-complete-external = {$attacker} shoots {REFLEXIVE($attacker)} in the head! 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-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}! suicide-popup-melee-initial-internal = You ready {THE($weapon)} against your throat. -suicide-popup-melee-initial-external = {$attacker} readies {POSS-ADJ(attacker)} {$weapon} against their throat. +suicide-popup-melee-initial-external = {$attacker} readies {POSS-ADJ($attacker)} {$weapon} against {POSS-ADJ($attacker)} throat. suicide-popup-melee-complete-internal = You slit your throat with {THE($weapon)}. suicide-popup-melee-complete-external = {$attacker} slits their throat with {THE($weapon)}. \ No newline at end of file From a51d885a422483f29ae1a067dbc426349e68076a Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 00:23:42 +1100 Subject: [PATCH 15/25] Popups for successful gun executions --- Content.Server/Execution/ExecutionSystem.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 8b863b453e20..df09f0755d45 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -44,7 +44,7 @@ public sealed class ExecutionSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audioSystem = default!; private const float MeleeExecutionTimeModifier = 5.0f; - private const float GunExecutionTime = 10.0f; + private const float GunExecutionTime = 6.0f; private const float DamageModifier = 9.0f; /// @@ -365,6 +365,18 @@ private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent ar // Gun successfully fired, deal damage _damageableSystem.TryChangeDamage(victim, damage * DamageModifier, true); _audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, false, AudioParams.Default); + + // Popups + if (attacker != victim) + { + ShowExecutionPopup("execution-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("execution-popup-gun-complete-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); + } + else + { + ShowExecutionPopup("suicide-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); + ShowExecutionPopup("suicide-popup-gun-complete-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); + } } private void ShowExecutionPopup(string locString, Filter filter, PopupType type, From 0027ca6223652a663099226b5408a23d20a19378 Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 00:33:15 +1100 Subject: [PATCH 16/25] whoops --- Content.Server/Execution/ExecutionSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index df09f0755d45..545a2a1c3a43 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -71,7 +71,7 @@ private void OnGetInteractionVerbsMelee( var weapon = args.Using!.Value; var victim = args.Target; - if (!CanExecuteWithMelee(weapon, victim, victim)) + if (!CanExecuteWithMelee(weapon, victim, attacker)) return; UtilityVerb verb = new() @@ -100,7 +100,7 @@ private void OnGetInteractionVerbsGun( var weapon = args.Using!.Value; var victim = args.Target; - if (!CanExecuteWithGun(weapon, victim, victim)) + if (!CanExecuteWithGun(weapon, victim, attacker)) return; UtilityVerb verb = new() From 96aa21af1eced89e04ab30c58a460de999833055 Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 09:36:00 +1100 Subject: [PATCH 17/25] Stop flare guns crashing the game on executions --- Content.Server/Execution/ExecutionSystem.cs | 24 +++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 545a2a1c3a43..5dcdeaaa2c64 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -314,29 +314,35 @@ private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent ar // Information about the ammo like damage SoundSpecifier? shootSound = component.SoundGunshot; - DamageSpecifier? damage = null; + DamageSpecifier damage = new DamageSpecifier(); // Get some information from IShootable - var ammo_uid = ev.Ammo[0].Entity; + var ammoUid = ev.Ammo[0].Entity; switch (ev.Ammo[0].Shootable) { case CartridgeAmmoComponent cartridge: // Get the damage value var prototype = _prototypeManager.Index(cartridge.Prototype); prototype.TryGetComponent(out var projectileA, _componentFactory); // sloth forgive me - damage = projectileA!.Damage * cartridge.Count; - + if (projectileA != null) + { + damage = projectileA.Damage * cartridge.Count; + } + // Expend the cartridge cartridge.Spent = true; - _appearanceSystem.SetData(ammo_uid!.Value, AmmoVisuals.Spent, true); - Dirty(ammo_uid.Value, cartridge); + _appearanceSystem.SetData(ammoUid!.Value, AmmoVisuals.Spent, true); + Dirty(ammoUid.Value, cartridge); break; case AmmoComponent newAmmo: - TryComp(ammo_uid, out var projectileB); - damage = projectileB!.Damage; - Del(ammo_uid); + TryComp(ammoUid, out var projectileB); + if (projectileB != null) + { + damage = projectileB.Damage; + } + Del(ammoUid); break; case HitscanPrototype hitscan: From 2caac9e82ac61ebd0814937bbc8e3a76f45d97be Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 09:55:23 +1100 Subject: [PATCH 18/25] Various tweaks --- Content.Server/Execution/ExecutionSystem.cs | 22 +++++++++++-------- .../Locale/en-US/execution/execution.ftl | 1 + 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 5dcdeaaa2c64..2360e4bb18f9 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -310,10 +310,14 @@ private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent ar RaiseLocalEvent(weapon, ev); // Check if there's any ammo left - if (ev.Ammo.Count <= 0) return; + if (ev.Ammo.Count <= 0) + { + _audioSystem.PlayEntity(component.SoundEmpty, Filter.Pvs(weapon), weapon, true, AudioParams.Default); + ShowExecutionPopup("execution-popup-gun-empty", Filter.Pvs(weapon), PopupType.Medium, attacker, victim, weapon); + return; + } // Information about the ammo like damage - SoundSpecifier? shootSound = component.SoundGunshot; DamageSpecifier damage = new DamageSpecifier(); // Get some information from IShootable @@ -353,17 +357,17 @@ private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent ar throw new ArgumentOutOfRangeException(); } - // Check for clumsiness + // Clumsy people have a chance to shoot themselves if (TryComp(attacker, out var clumsy) && component.ClumsyProof == false) { if (_interactionSystem.TryRollClumsy(attacker, 0.33333333f, clumsy)) { - _damageableSystem.TryChangeDamage(attacker, damage, origin: attacker); - ShowExecutionPopup("execution-popup-gun-clumsy-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); ShowExecutionPopup("execution-popup-gun-clumsy-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); - _audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, false, AudioParams.Default); + // You shoot yourself with the gun (no damage multiplier) + _damageableSystem.TryChangeDamage(attacker, damage, origin: attacker); + _audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, true, AudioParams.Default); return; } } @@ -376,12 +380,12 @@ private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent ar if (attacker != victim) { ShowExecutionPopup("execution-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("execution-popup-gun-complete-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); + ShowExecutionPopup("execution-popup-gun-complete-external", Filter.PvsExcept(attacker), PopupType.LargeCaution, attacker, victim, weapon); } else { - ShowExecutionPopup("suicide-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.Medium, attacker, victim, weapon); - ShowExecutionPopup("suicide-popup-gun-complete-external", Filter.PvsExcept(attacker), PopupType.MediumCaution, attacker, victim, weapon); + ShowExecutionPopup("suicide-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.LargeCaution, attacker, victim, weapon); + ShowExecutionPopup("suicide-popup-gun-complete-external", Filter.PvsExcept(attacker), PopupType.LargeCaution, attacker, victim, weapon); } } diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl index 8a013ea9fa55..044b31e94c20 100644 --- a/Resources/Locale/en-US/execution/execution.ftl +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -12,6 +12,7 @@ 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 = {THE($weapon)} clicks. suicide-popup-gun-initial-internal = You place the muzzle of {THE($weapon)} in your mouth. suicide-popup-gun-initial-external = {$attacker} places the muzzle of {THE($weapon)} in {POSS-ADJ($attacker)} mouth. From e1d6fd2a4d794b4942e87977a48a71e99302c58b Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 10:00:53 +1100 Subject: [PATCH 19/25] Remove some old usings --- Content.Server/Execution/ExecutionSystem.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 2360e4bb18f9..c664013cb7db 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -1,7 +1,5 @@ -using System.Numerics; using Content.Server.Interaction; using Content.Server.Kitchen.Components; -using Content.Server.Weapons.Ranged.Systems; using Content.Shared.ActionBlocker; using Content.Shared.Damage; using Content.Shared.Database; @@ -20,7 +18,6 @@ using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; -using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Prototypes; From 97806a0818552685dd7055a936abafa2fe1e10d7 Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 10:13:23 +1100 Subject: [PATCH 20/25] Pacifists can no longer execute --- Content.Server/Execution/ExecutionSystem.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index c664013cb7db..45e0fc8e5b30 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -114,7 +114,7 @@ private void OnGetInteractionVerbsGun( args.Verbs.Add(verb); } - private bool CanExecuteWithAny(EntityUid weapon, EntityUid victim, EntityUid user) + private bool CanExecuteWithAny(EntityUid weapon, EntityUid victim, EntityUid attacker) { // No point executing someone if they can't take damage if (!TryComp(victim, out var damage)) @@ -127,13 +127,17 @@ private bool CanExecuteWithAny(EntityUid weapon, EntityUid victim, EntityUid use // 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 != user && _actionBlockerSystem.CanInteract(victim, null)) + if (victim != attacker && _actionBlockerSystem.CanInteract(victim, null)) return false; // You must be not incapacitated to execute yourself - if (victim == user && !_actionBlockerSystem.CanInteract(victim, null)) + if (victim == attacker && !_actionBlockerSystem.CanInteract(victim, null)) return false; // All checks passed From 1195e30f7886330432c21323528a4c57df1a2d63 Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 10:17:23 +1100 Subject: [PATCH 21/25] Remove unnecessary check --- Content.Server/Execution/ExecutionSystem.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 45e0fc8e5b30..2bd0d2831499 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -135,10 +135,6 @@ private bool CanExecuteWithAny(EntityUid weapon, EntityUid victim, EntityUid att // The victim must be incapacitated to be executed if (victim != attacker && _actionBlockerSystem.CanInteract(victim, null)) return false; - - // You must be not incapacitated to execute yourself - if (victim == attacker && !_actionBlockerSystem.CanInteract(victim, null)) - return false; // All checks passed return true; From 4e78da83de88db0d5227b23dedf51c3dd0b4c747 Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 10:23:01 +1100 Subject: [PATCH 22/25] Use CanShoot in gunsystem --- Content.Server/Execution/ExecutionSystem.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 2bd0d2831499..4749bb463e06 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Interaction; using Content.Server.Kitchen.Components; +using Content.Server.Weapons.Ranged.Systems; using Content.Shared.ActionBlocker; using Content.Shared.Damage; using Content.Shared.Database; @@ -39,6 +40,7 @@ public sealed class ExecutionSystem : EntitySystem [Dependency] private readonly IComponentFactory _componentFactory = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly GunSystem _gunSystem = default!; private const float MeleeExecutionTimeModifier = 5.0f; private const float GunExecutionTime = 6.0f; @@ -155,8 +157,8 @@ private bool CanExecuteWithGun(EntityUid weapon, EntityUid victim, EntityUid use { if (!CanExecuteWithAny(weapon, victim, user)) return false; - // We must be able to actually fire the gun and have it do damage - if (!TryComp(weapon, out var gun)) + // We must be able to actually fire the gun + if (!TryComp(weapon, out var gun) && _gunSystem.CanShoot(gun!)) return false; return true; From 688490616e52ea9255764487e9a9461f39880da8 Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 14:04:46 +1100 Subject: [PATCH 23/25] Capitalisation in ftl string --- Resources/Locale/en-US/execution/execution.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl index 044b31e94c20..f3aab6fb050e 100644 --- a/Resources/Locale/en-US/execution/execution.ftl +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -12,7 +12,7 @@ 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 = {THE($weapon)} clicks. +execution-popup-gun-empty = {CAPITALIZE(THE($weapon))} clicks. suicide-popup-gun-initial-internal = You place the muzzle of {THE($weapon)} in your mouth. suicide-popup-gun-initial-external = {$attacker} places the muzzle of {THE($weapon)} in {POSS-ADJ($attacker)} mouth. From 3fa4c61be73d7251dd7f903e5ecdec24e5d3946d Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 17:29:44 +1100 Subject: [PATCH 24/25] Fix melee executions not playing a sound --- Content.Server/Execution/ExecutionSystem.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 4749bb463e06..4354608ca3e4 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -35,7 +35,6 @@ public sealed class ExecutionSystem : EntitySystem [Dependency] private readonly InteractionSystem _interactionSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly SharedMeleeWeaponSystem _meleeSystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IComponentFactory _componentFactory = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; @@ -249,7 +248,7 @@ private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEven return; _damageableSystem.TryChangeDamage(victim, melee.Damage * DamageModifier, true); - _meleeSystem.PlayHitSound(victim, weapon, null, null, null); + _audioSystem.PlayEntity(melee.HitSound, Filter.Pvs(weapon), weapon, true, AudioParams.Default); if (attacker == victim) { From 4a07b70e938c912eab1e3a5644536b0376f5a8d2 Mon Sep 17 00:00:00 2001 From: veritius Date: Wed, 17 Jan 2024 17:40:50 +1100 Subject: [PATCH 25/25] localisation tweaks --- Resources/Locale/en-US/execution/execution.ftl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl index f3aab6fb050e..8bdf32616666 100644 --- a/Resources/Locale/en-US/execution/execution.ftl +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -12,7 +12,7 @@ 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-gun-empty = {CAPITALIZE(THE($weapon))} clicks. suicide-popup-gun-initial-internal = You place the muzzle of {THE($weapon)} in your mouth. suicide-popup-gun-initial-external = {$attacker} places the muzzle of {THE($weapon)} in {POSS-ADJ($attacker)} mouth. @@ -26,5 +26,5 @@ execution-popup-melee-complete-external = {$attacker} slits the throat of {$vict suicide-popup-melee-initial-internal = You ready {THE($weapon)} against your throat. suicide-popup-melee-initial-external = {$attacker} readies {POSS-ADJ($attacker)} {$weapon} against {POSS-ADJ($attacker)} throat. -suicide-popup-melee-complete-internal = You slit your throat with {THE($weapon)}. -suicide-popup-melee-complete-external = {$attacker} slits their throat with {THE($weapon)}. \ No newline at end of file +suicide-popup-melee-complete-internal = You slit your throat with {THE($weapon)}! +suicide-popup-melee-complete-external = {$attacker} slits {POSS-ADJ($attacker)} throat with {THE($weapon)}! \ No newline at end of file