diff --git a/Content.Server/Explosion/Components/TriggerOnMobstateChangeComponent.cs b/Content.Server/Explosion/Components/TriggerOnMobstateChangeComponent.cs index 3117c956b5..e5d8c6c9bf 100644 --- a/Content.Server/Explosion/Components/TriggerOnMobstateChangeComponent.cs +++ b/Content.Server/Explosion/Components/TriggerOnMobstateChangeComponent.cs @@ -13,7 +13,7 @@ public sealed class TriggerOnMobstateChangeComponent : Component /// [ViewVariables] [DataField("mobState", required: true)] - public MobState MobState = MobState.Alive; + public List MobState = new(); /// /// If true, prevents suicide attempts for the trigger to prevent cheese. diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.Mobstate.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.Mobstate.cs index d75b0ce43d..0b674adefd 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.Mobstate.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.Mobstate.cs @@ -18,7 +18,7 @@ public sealed partial class TriggerSystem private void OnMobStateChanged(EntityUid uid, TriggerOnMobstateChangeComponent component, MobStateChangedEvent args) { - if (component.MobState != args.NewMobState) + if (!component.MobState.Contains(args.NewMobState)) return; //This chains Mobstate Changed triggers with OnUseTimerTrigger if they have it diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index ec1d42493b..eb2d3b6a0d 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -5,10 +5,13 @@ using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Explosion.Components; using Content.Server.Flash; using Content.Server.Flash.Components; +using Content.Server.Radio.EntitySystems; using Content.Shared.Database; using Content.Shared.Implants.Components; using Content.Shared.Interaction; using Content.Shared.Payload.Components; +using Robust.Shared.Prototypes; +using Content.Shared.Radio; using Content.Shared.Slippery; using Content.Shared.StepTrigger.Systems; using Content.Shared.Trigger; @@ -17,6 +20,8 @@ using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; using Robust.Shared.Player; namespace Content.Server.Explosion.EntitySystems @@ -53,6 +58,8 @@ namespace Content.Server.Explosion.EntitySystems [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly BodySystem _body = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly RadioSystem _radioSystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public override void Initialize() { @@ -76,6 +83,7 @@ namespace Content.Server.Explosion.EntitySystems SubscribeLocalEvent(HandleExplodeTrigger); SubscribeLocalEvent(HandleFlashTrigger); SubscribeLocalEvent(HandleGibTrigger); + SubscribeLocalEvent(HandleRattleTrigger); } private void OnSpawnTrigger(EntityUid uid, SpawnOnTriggerComponent component, TriggerEvent args) @@ -121,11 +129,40 @@ namespace Content.Server.Explosion.EntitySystems args.Handled = true; } + private void HandleRattleTrigger(EntityUid uid, RattleComponent component, TriggerEvent args) + { + if (!TryComp(uid, out var implanted)) + return; + + if (implanted.ImplantedEntity == null) + return; + + // Gets location of the implant + var ownerXform = Transform(uid); + var pos = ownerXform.MapPosition; + var x = (int) pos.X; + var y = (int) pos.Y; + var posText = $"({x}, {y})"; + + var critMessage = Loc.GetString(component.CritMessage, ("user", implanted.ImplantedEntity.Value), ("position", posText)); + var deathMessage = Loc.GetString(component.DeathMessage, ("user", implanted.ImplantedEntity.Value), ("position", posText)); + + if (!TryComp(implanted.ImplantedEntity, out var mobstate)) + return; + + // Sends a message to the radio channel specified by the implant + if (mobstate.CurrentState == MobState.Critical) + _radioSystem.SendRadioMessage(uid, critMessage, _prototypeManager.Index(component.RadioChannel), uid); + if (mobstate.CurrentState == MobState.Dead) + _radioSystem.SendRadioMessage(uid, deathMessage, _prototypeManager.Index(component.RadioChannel), uid); + + args.Handled = true; + } private void OnTriggerCollide(EntityUid uid, TriggerOnCollideComponent component, ref StartCollideEvent args) { - if(args.OurFixture.ID == component.FixtureID && (!component.IgnoreOtherNonHard || args.OtherFixture.Hard)) - Trigger(component.Owner); + if (args.OurFixture.ID == component.FixtureID && (!component.IgnoreOtherNonHard || args.OtherFixture.Hard)) + Trigger(component.Owner); } private void OnActivate(EntityUid uid, TriggerOnActivateComponent component, ActivateInWorldEvent args) diff --git a/Content.Shared/Implants/Components/RattleComponent.cs b/Content.Shared/Implants/Components/RattleComponent.cs new file mode 100644 index 0000000000..310e842104 --- /dev/null +++ b/Content.Shared/Implants/Components/RattleComponent.cs @@ -0,0 +1,20 @@ +using Content.Shared.Radio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Implants.Components; + +[RegisterComponent] +public sealed class RattleComponent : Component +{ + // The radio channel the message will be sent to + [DataField("radioChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string RadioChannel = "Syndicate"; + + // The message that the implant will send when crit + [DataField("critMessage")] + public string CritMessage = "deathrattle-implant-critical-message"; + + // The message that the implant will send when dead + [DataField("deathMessage")] + public string DeathMessage = "deathrattle-implant-dead-message"; +} diff --git a/Content.Shared/Implants/Components/SubdermalImplantComponent.cs b/Content.Shared/Implants/Components/SubdermalImplantComponent.cs index ea0b07001d..a7dd56f2a0 100644 --- a/Content.Shared/Implants/Components/SubdermalImplantComponent.cs +++ b/Content.Shared/Implants/Components/SubdermalImplantComponent.cs @@ -1,4 +1,6 @@ using Content.Shared.Actions; +using Content.Shared.Radio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Implants.Components; diff --git a/Resources/Locale/en-US/implant/implant.ftl b/Resources/Locale/en-US/implant/implant.ftl index e06cbf5664..2ed667bedf 100644 --- a/Resources/Locale/en-US/implant/implant.ftl +++ b/Resources/Locale/en-US/implant/implant.ftl @@ -29,3 +29,8 @@ open-uplink-implant-action-description = Opens the syndicate uplink embedded und action-name-honk = Honk! action-desc-honk = Activates your honking implant, which will produce the signature sound of the clown. + +## Implanter Messages + +deathrattle-implant-dead-message = {$user} has died at {$position}. +deathrattle-implant-critical-message = {$user} life signs critical, immediate assistance required at {$position}. diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 622d6fe18a..5fb96cede8 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -115,6 +115,9 @@ uplink-macro-bomb-implanter-desc = Inject this and on death you'll create a larg uplink-uplink-implanter-name = Uplink Implanter uplink-uplink-implanter-desc = Stealthily order equipment without the need for a PDA. Swallow telecrystals to top up the uplink. +uplink-deathrattle-implant-name = Box Of Deathrattle Implants +uplink-deathrattle-implant-desc = A box containing enough deathrattle implants for the whole squad. Relays a message containing your position to the syndicate channel when you go into a critical state or die. + # Bundles uplink-c20r-bundle-name = C-20r Bundle uplink-c20r-bundle-desc = Old faithful: The classic C-20r Submachine Gun, bundled with three magazines. diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml index ab14ccfdaa..4694966232 100644 --- a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml +++ b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml @@ -393,3 +393,24 @@ amount: 2 - type: Storage capacity: 15 + +- type: entity + name: deathrattle implant box + parent: BoxCardboard + id: BoxDeathRattleImplants + description: Six deathrattle implants and handheld GPS devices for the whole squad. + components: + - type: Item + size: 60 + - type: StorageFill + contents: + - id: DeathRattleImplanter + amount: 6 + - id: HandheldGPSBasic + amount: 6 + - type: Storage + capacity: 60 + - type: Sprite + layers: + - state: box + - state: syringe diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index c6983f1300..07d637b218 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -434,6 +434,25 @@ tags: - NukeOpsUplink +- type: listing + id: UplinkDeathRattle + name: uplink-deathrattle-implant-name + description: uplink-deathrattle-implant-desc + productEntity: BoxDeathRattleImplants + cost: + Telecrystal: 4 + categories: + - UplinkImplants + conditions: + - !type:StoreWhitelistCondition + whitelist: + tags: + - NukeOpsUplink + - !type:BuyerWhitelistCondition + blacklist: + components: + - SurplusBundle + # Bundles - type: listing diff --git a/Resources/Prototypes/Entities/Objects/Misc/implanters.yml b/Resources/Prototypes/Entities/Objects/Misc/implanters.yml index 6dc268c182..1086432385 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/implanters.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/implanters.yml @@ -160,3 +160,12 @@ components: - type: Implanter implant: MacroBombImplant + +- type: entity + id: DeathRattleImplanter + name: Deathrattle implanter + description: a single use implanter, the implant will inform anyone with access to the syndicate radio channel of your death. + parent: BaseImplantOnlyImplanter + components: + - type: Implanter + implant: DeathRattleImplant diff --git a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml index bec249b5f3..6ecd8860f9 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml @@ -22,7 +22,8 @@ components: - type: SubdermalImplant - type: TriggerOnMobstateChange - mobState: Dead + mobState: + - Dead - type: EmitSoundOnTrigger sound: collection: SadTrombone @@ -97,6 +98,11 @@ - type: StationLimitedNetwork - type: WirelessNetworkConnection range: 500 + - type: TriggerOnMobstateChange + mobState: + - Critical + - type: Rattle + radioChannel: "Security" #Traitor implants @@ -163,7 +169,8 @@ permanent: true implantAction: ActivateMicroBomb - type: TriggerOnMobstateChange - mobState: Dead + mobState: + - Dead - type: TriggerImplantAction - type: ExplodeOnTrigger - type: GibOnTrigger @@ -191,7 +198,8 @@ - type: SubdermalImplant permanent: true - type: TriggerOnMobstateChange #Chains with OnUseTimerTrigger - mobState: Dead + mobState: + - Dead preventSuicide: true - type: OnUseTimerTrigger delay: 7 @@ -213,3 +221,19 @@ - SubdermalImplant - HideContextMenu - MacroBomb + +- type: entity + parent: BaseSubdermalImplant + id: DeathRattleImplant + name: deathrattle implant + description: sends a message with your location to the syndicate radio channel when you fall into a critical state or die. + noSpawn: true + components: + - type: SubdermalImplant + permanent: true + - type: TriggerOnMobstateChange + mobState: + - Critical + - Dead + - type: Rattle +