From 21c1cb57ce21505102a614120f7e53df70c76bdc Mon Sep 17 00:00:00 2001 From: keronshb <54602815+keronshb@users.noreply.github.com> Date: Sun, 20 Nov 2022 21:51:44 -0500 Subject: [PATCH] Makes adjustment to macro bomb implants and how mobstate trigger handles suicide (#12682) --- Content.Server/Chat/SuicideSystem.cs | 44 ++++++++++------ .../TriggerOnMobstateChangeComponent.cs | 8 +++ .../EntitySystems/TriggerSystem.Mobstate.cs | 50 +++++++++++++++++++ .../EntitySystems/TriggerSystem.OnUse.cs | 6 +-- .../Explosion/EntitySystems/TriggerSystem.cs | 41 ++------------- .../Implants/SubdermalImplantSystem.cs | 2 + .../Interaction/Events/SuicideEvent.cs | 13 ++++- Resources/Locale/en-US/suicide/suicide.ftl | 1 + .../Prototypes/Catalog/uplink_catalog.yml | 10 ++-- .../Objects/Misc/subdermal_implants.yml | 9 ++-- .../Entities/Objects/Specific/syndicate.yml | 6 --- Resources/Prototypes/tags.yml | 3 -- 12 files changed, 122 insertions(+), 71 deletions(-) create mode 100644 Content.Server/Explosion/EntitySystems/TriggerSystem.Mobstate.cs create mode 100644 Resources/Locale/en-US/suicide/suicide.ftl diff --git a/Content.Server/Chat/SuicideSystem.cs b/Content.Server/Chat/SuicideSystem.cs index 4344b98efa..bf7eaf4511 100644 --- a/Content.Server/Chat/SuicideSystem.cs +++ b/Content.Server/Chat/SuicideSystem.cs @@ -27,26 +27,27 @@ namespace Content.Server.Chat { // Checks to see if the CannotSuicide tag exits, ghosts instead. if (_tagSystem.HasTag(victim, "CannotSuicide")) - { return false; - } // Checks to see if the player is dead. if (!TryComp(victim, out var mobState) || _mobState.IsDead(victim, mobState)) - { return false; - } - _adminLogger.Add(LogType.Suicide, - $"{EntityManager.ToPrettyString(victim):player} is committing suicide"); + _adminLogger.Add(LogType.Suicide, $"{EntityManager.ToPrettyString(victim):player} is committing suicide"); var suicideEvent = new SuicideEvent(victim); + //Check to see if there were any systems blocking this suicide + if (SuicideAttemptBlocked(victim, suicideEvent)) + return false; + // If you are critical, you wouldn't be able to use your surroundings to suicide, so you do the default suicide if (!_mobState.IsCritical(victim, mobState)) - { EnvironmentSuicideHandler(victim, suicideEvent); - } + + if (suicideEvent.AttemptBlocked) + return false; + DefaultSuicideHandler(victim, suicideEvent); ApplyDeath(victim, suicideEvent.Kind!.Value); @@ -58,7 +59,9 @@ namespace Content.Server.Chat /// private static void DefaultSuicideHandler(EntityUid victim, SuicideEvent suicideEvent) { - if (suicideEvent.Handled) return; + if (suicideEvent.Handled) + return; + var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", victim)); victim.PopupMessageOtherClients(othersMessage); @@ -68,10 +71,26 @@ namespace Content.Server.Chat } /// - /// Raise event to attempt to use held item, or surrounding entities to commit suicide + /// Checks to see if there are any other systems that prevent suicide + /// + /// Returns true if there was a blocked attempt + private bool SuicideAttemptBlocked(EntityUid victim, SuicideEvent suicideEvent) + { + RaiseLocalEvent(victim, suicideEvent, false); + + if (suicideEvent.AttemptBlocked) + return true; + + return false; + } + + /// + /// Raise event to attempt to use held item, or surrounding entities to attempt to commit suicide /// private void EnvironmentSuicideHandler(EntityUid victim, SuicideEvent suicideEvent) { + var itemQuery = GetEntityQuery(); + // Suicide by held item if (EntityManager.TryGetComponent(victim, out HandsComponent? handsComponent) && handsComponent.ActiveHandEntity is { } item) @@ -82,8 +101,6 @@ namespace Content.Server.Chat return; } - var itemQuery = GetEntityQuery(); - // Suicide by nearby entity (ex: Microwave) foreach (var entity in _entityLookupSystem.GetEntitiesInRange(victim, 1, LookupFlags.Approximate | LookupFlags.Anchored)) { @@ -106,8 +123,7 @@ namespace Content.Server.Chat if (!_prototypeManager.TryIndex(kind.ToString(), out var damagePrototype)) { const SuicideKind fallback = SuicideKind.Blunt; - Logger.Error( - $"{nameof(SuicideSystem)} could not find the damage type prototype associated with {kind}. Falling back to {fallback}"); + Logger.Error($"{nameof(SuicideSystem)} could not find the damage type prototype associated with {kind}. Falling back to {fallback}"); damagePrototype = _prototypeManager.Index(fallback.ToString()); } const int lethalAmountOfDamage = 200; // TODO: Would be nice to get this number from somewhere else diff --git a/Content.Server/Explosion/Components/TriggerOnMobstateChangeComponent.cs b/Content.Server/Explosion/Components/TriggerOnMobstateChangeComponent.cs index ded8736e20..657de34ffe 100644 --- a/Content.Server/Explosion/Components/TriggerOnMobstateChangeComponent.cs +++ b/Content.Server/Explosion/Components/TriggerOnMobstateChangeComponent.cs @@ -11,6 +11,14 @@ public sealed class TriggerOnMobstateChangeComponent : Component /// /// What state should trigger this? /// + [ViewVariables] [DataField("mobState", required: true)] public DamageState MobState = DamageState.Alive; + + /// + /// If true, prevents suicide attempts for the trigger to prevent cheese. + /// + [ViewVariables] + [DataField("preventSuicide")] + public bool PreventSuicide = false; } diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.Mobstate.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.Mobstate.cs new file mode 100644 index 0000000000..8dbe52a512 --- /dev/null +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.Mobstate.cs @@ -0,0 +1,50 @@ +using Content.Server.Explosion.Components; +using Content.Shared.Interaction.Events; +using Content.Shared.MobState; +using Robust.Shared.Player; + +namespace Content.Server.Explosion.EntitySystems; + +public sealed partial class TriggerSystem +{ + private void InitializeMobstate() + { + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnSuicide); + } + + private void OnMobStateChanged(EntityUid uid, TriggerOnMobstateChangeComponent component, MobStateChangedEvent args) + { + if (component.MobState < args.CurrentMobState) + return; + + //This chains Mobstate Changed triggers with OnUseTimerTrigger if they have it + //Very useful for things that require a mobstate change and a timer + if (TryComp(uid, out var timerTrigger)) + { + HandleTimerTrigger( + uid, + args.Origin, + timerTrigger.Delay, + timerTrigger.BeepInterval, + timerTrigger.InitialBeepDelay, + timerTrigger.BeepSound, + timerTrigger.BeepParams); + } + + else + Trigger(uid); + } + + private void OnSuicide(EntityUid uid, TriggerOnMobstateChangeComponent component, SuicideEvent args) + { + if (args.Handled) + return; + + if (component.PreventSuicide) + { + _popupSystem.PopupEntity(Loc.GetString("suicide-prevented"), args.Victim, Filter.Entities(args.Victim)); + args.BlockSuicideAttempt(component.PreventSuicide); + } + } +} diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.OnUse.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.OnUse.cs index 5b3370b1c2..05c8ec68aa 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.OnUse.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.OnUse.cs @@ -1,9 +1,8 @@ using Content.Server.Explosion.Components; using Content.Server.Sticky.Events; using Content.Shared.Examine; -using Content.Shared.Popups; using Content.Shared.Interaction.Events; -using Content.Shared.MobState; +using Content.Shared.Popups; using Content.Shared.Verbs; using Robust.Shared.Player; @@ -142,7 +141,8 @@ public sealed partial class TriggerSystem private void OnTimerUse(EntityUid uid, OnUseTimerTriggerComponent component, UseInHandEvent args) { - if (args.Handled) return; + if (args.Handled) + return; HandleTimerTrigger( uid, diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index 4e86cc6dab..a9b0afa5f1 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -5,26 +5,18 @@ using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Explosion.Components; using Content.Server.Flash; using Content.Server.Flash.Components; -using Content.Server.Sticky.Events; -using Content.Shared.Actions; -using Content.Shared.Body.Components; -using JetBrains.Annotations; -using Robust.Shared.Audio; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Dynamics; -using Robust.Shared.Player; -using Content.Shared.Trigger; using Content.Shared.Database; -using Content.Shared.Explosion; using Content.Shared.Implants.Components; using Content.Shared.Interaction; -using Content.Shared.MobState; using Content.Shared.Payload.Components; using Content.Shared.StepTrigger.Systems; -using Robust.Server.Containers; +using Content.Shared.Trigger; +using JetBrains.Annotations; +using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; +using Robust.Shared.Player; namespace Content.Server.Explosion.EntitySystems { @@ -63,12 +55,12 @@ namespace Content.Server.Explosion.EntitySystems InitializeSignal(); InitializeTimedCollide(); InitializeVoice(); + InitializeMobstate(); SubscribeLocalEvent(OnTriggerCollide); SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(OnImplantTrigger); SubscribeLocalEvent(OnStepTriggered); - SubscribeLocalEvent(OnMobStateChanged); SubscribeLocalEvent(HandleDeleteTrigger); SubscribeLocalEvent(HandleExplodeTrigger); @@ -130,29 +122,6 @@ namespace Content.Server.Explosion.EntitySystems Trigger(uid, args.Tripper); } - private void OnMobStateChanged(EntityUid uid, TriggerOnMobstateChangeComponent component, MobStateChangedEvent args) - { - if (component.MobState < args.CurrentMobState) - return; - - //This chains Mobstate Changed triggers with OnUseTimerTrigger if they have it - //Very useful for things that require a mobstate change and a timer - if (TryComp(uid, out var timerTrigger)) - { - HandleTimerTrigger( - uid, - args.Origin, - timerTrigger.Delay, - timerTrigger.BeepInterval, - timerTrigger.InitialBeepDelay, - timerTrigger.BeepSound, - timerTrigger.BeepParams); - } - - else - Trigger(uid); - } - public bool Trigger(EntityUid trigger, EntityUid? user = null) { var triggerEvent = new TriggerEvent(trigger, user); diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 4ead1775f7..095a115804 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Cuffs.Components; using Content.Shared.Implants; using Content.Shared.Implants.Components; +using Content.Shared.Interaction.Events; using Content.Shared.MobState; using Robust.Shared.Containers; @@ -17,6 +18,7 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem SubscribeLocalEvent(OnFreedomImplant); SubscribeLocalEvent(RelayToImplantEvent); + SubscribeLocalEvent(RelayToImplantEvent); } private void OnFreedomImplant(EntityUid uid, SubdermalImplantComponent component, UseFreedomImplantEvent args) diff --git a/Content.Shared/Interaction/Events/SuicideEvent.cs b/Content.Shared/Interaction/Events/SuicideEvent.cs index e0c5775261..7b9c1efe0d 100644 --- a/Content.Shared/Interaction/Events/SuicideEvent.cs +++ b/Content.Shared/Interaction/Events/SuicideEvent.cs @@ -11,12 +11,21 @@ namespace Content.Shared.Interaction.Events } public void SetHandled(SuicideKind kind) { - if (Handled) throw new InvalidOperationException("Suicide was already handled"); + if (Handled) + throw new InvalidOperationException("Suicide was already handled"); + Kind = kind; } - + + public void BlockSuicideAttempt(bool suicideAttempt) + { + if (suicideAttempt) + AttemptBlocked = suicideAttempt; + } + public SuicideKind? Kind { get; private set; } public EntityUid Victim { get; private set; } + public bool AttemptBlocked { get; private set; } public bool Handled => Kind != null; } diff --git a/Resources/Locale/en-US/suicide/suicide.ftl b/Resources/Locale/en-US/suicide/suicide.ftl new file mode 100644 index 0000000000..2207e22413 --- /dev/null +++ b/Resources/Locale/en-US/suicide/suicide.ftl @@ -0,0 +1 @@ +suicide-prevented = You tried to suicide, but only your spirit escapes. diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 150ddeeeda..f03148e732 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -321,14 +321,18 @@ description: Inject this and on death you'll create a large explosion. Huge team casualty cost, use at own risk. Replaces internal micro bomb. productEntity: MacroBombImplanter cost: - Telecrystal: 20 + Telecrystal: 25 categories: - UplinkUtility conditions: - !type:StoreWhitelistCondition - blacklist: + whitelist: tags: - - TraitorUplink + - NukeOpsUplink + - !type:BuyerWhitelistCondition + blacklist: + components: + - SurplusBundle # Bundles diff --git a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml index cd25cdd496..aac14ab62e 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml @@ -154,8 +154,9 @@ permanent: true - type: TriggerOnMobstateChange #Chains with OnUseTimerTrigger mobState: Dead + preventSuicide: true - type: OnUseTimerTrigger - delay: 5 + delay: 7 initialBeepDelay: 0 beepSound: /Audio/Machines/Nuke/general_beep.ogg - type: ExplodeOnTrigger @@ -163,9 +164,9 @@ deleteItems: true - type: Explosive explosionType: Default - totalIntensity: 4000 - intensitySlope: 5 - maxIntensity: 50 + totalIntensity: 3500 + intensitySlope: 15 + maxIntensity: 70 canCreateVacuum: true - type: Tag tags: diff --git a/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml b/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml index 9043ef148d..b477f40a4a 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml @@ -82,9 +82,6 @@ preset: StorePresetUplink balance: Telecrystal: 20 - - type: Tag - tags: - - TraitorUplink - type: entity parent: BaseUplinkRadio @@ -95,9 +92,6 @@ preset: StorePresetUplink balance: Telecrystal: 25 - - type: Tag - tags: - - TraitorUplink #this uplink MUST be used for nukeops, as it has the tag for filtering the listing. - type: entity diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index eb4fdfd837..63b7c08e69 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -380,9 +380,6 @@ - type: Tag id: NoSpinOnThrow -- type: Tag - id: TraitorUplink - - type: Tag id: NoBlockAnchoring