From da07d91895a7a7863a5ea2199c489dc882083a9e Mon Sep 17 00:00:00 2001 From: Kara Date: Wed, 1 Jun 2022 01:39:06 -0700 Subject: [PATCH] Modular landmines (#8351) --- .../Destructible/DestructibleSystem.cs | 1 + .../Thresholds/Behaviors/TriggerBehavior.cs | 10 ++++ .../Explosion/EntitySystems/TriggerSystem.cs | 8 ++- .../GhostKick/GhostKickUserOnTriggerSystem.cs | 2 + Content.Server/LandMines/LandMineComponent.cs | 3 +- Content.Server/LandMines/LandMineSystem.cs | 16 +++--- .../Payload/EntitySystems/PayloadSystem.cs | 46 ++++++++++++++-- Content.Server/Sound/EmitSoundSystem.cs | 1 + .../Locale/en-US/payload/payload-case.ftl | 3 + .../Entities/Objects/Misc/land_mine.yml | 28 +++++++++- .../Objects/Weapons/Throwable/grenades.yml | 8 +++ .../Graphs/weapons/modular_mine.yml | 55 +++++++++++++++++++ .../Recipes/Construction/modular.yml | 25 +++++++++ .../Recipes/Construction/modular_grenades.yml | 12 ---- 14 files changed, 190 insertions(+), 28 deletions(-) create mode 100644 Content.Server/Destructible/Thresholds/Behaviors/TriggerBehavior.cs create mode 100644 Resources/Locale/en-US/payload/payload-case.ftl create mode 100644 Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_mine.yml create mode 100644 Resources/Prototypes/Recipes/Construction/modular.yml delete mode 100644 Resources/Prototypes/Recipes/Construction/modular_grenades.yml diff --git a/Content.Server/Destructible/DestructibleSystem.cs b/Content.Server/Destructible/DestructibleSystem.cs index 0697872314..768bdee6d3 100644 --- a/Content.Server/Destructible/DestructibleSystem.cs +++ b/Content.Server/Destructible/DestructibleSystem.cs @@ -24,6 +24,7 @@ namespace Content.Server.Destructible [Dependency] public readonly ConstructionSystem ConstructionSystem = default!; [Dependency] public readonly ExplosionSystem ExplosionSystem = default!; [Dependency] public readonly StackSystem StackSystem = default!; + [Dependency] public readonly TriggerSystem TriggerSystem = default!; [Dependency] public readonly IPrototypeManager PrototypeManager = default!; [Dependency] public readonly IComponentFactory ComponentFactory = default!; diff --git a/Content.Server/Destructible/Thresholds/Behaviors/TriggerBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/TriggerBehavior.cs new file mode 100644 index 0000000000..3f6c976e0f --- /dev/null +++ b/Content.Server/Destructible/Thresholds/Behaviors/TriggerBehavior.cs @@ -0,0 +1,10 @@ +namespace Content.Server.Destructible.Thresholds.Behaviors; + +[DataDefinition] +public sealed class TriggerBehavior : IThresholdBehavior +{ + public void Execute(EntityUid owner, DestructibleSystem system) + { + system.TriggerSystem.Trigger(owner); + } +} diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index f04623e2d2..a39e78a1d5 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -12,6 +12,7 @@ using Robust.Shared.Player; using Content.Shared.Sound; using Content.Shared.Trigger; using Content.Shared.Database; +using Content.Shared.Explosion; using Content.Shared.Interaction; namespace Content.Server.Explosion.EntitySystems @@ -60,6 +61,7 @@ namespace Content.Server.Explosion.EntitySystems private void HandleExplodeTrigger(EntityUid uid, ExplodeOnTriggerComponent component, TriggerEvent args) { _explosions.TriggerExplosive(uid, user: args.User); + args.Handled = true; } #region Flash @@ -67,12 +69,14 @@ namespace Content.Server.Explosion.EntitySystems { // TODO Make flash durations sane ffs. _flashSystem.FlashArea(uid, args.User, component.Range, component.Duration * 1000f); + args.Handled = true; } #endregion private void HandleDeleteTrigger(EntityUid uid, DeleteOnTriggerComponent component, TriggerEvent args) { EntityManager.QueueDeleteEntity(uid); + args.Handled = true; } private void OnTriggerCollide(EntityUid uid, TriggerOnCollideComponent component, StartCollideEvent args) @@ -84,12 +88,14 @@ namespace Content.Server.Explosion.EntitySystems private void OnActivate(EntityUid uid, TriggerOnActivateComponent component, ActivateInWorldEvent args) { Trigger(component.Owner, args.User); + args.Handled = true; } - public void Trigger(EntityUid trigger, EntityUid? user = null) + public bool Trigger(EntityUid trigger, EntityUid? user = null) { var triggerEvent = new TriggerEvent(trigger, user); EntityManager.EventBus.RaiseLocalEvent(trigger, triggerEvent); + return triggerEvent.Handled; } public void HandleTimerTrigger(EntityUid uid, EntityUid? user, float delay , float beepInterval, float? initialBeepDelay, SoundSpecifier? beepSound, AudioParams beepParams) diff --git a/Content.Server/GhostKick/GhostKickUserOnTriggerSystem.cs b/Content.Server/GhostKick/GhostKickUserOnTriggerSystem.cs index 8306400c34..bbf7813c16 100644 --- a/Content.Server/GhostKick/GhostKickUserOnTriggerSystem.cs +++ b/Content.Server/GhostKick/GhostKickUserOnTriggerSystem.cs @@ -20,5 +20,7 @@ public sealed class GhostKickUserOnTriggerSystem : EntitySystem _ghostKickManager.DoDisconnect( actor.PlayerSession.ConnectedClient, "Tripped over a kick mine, crashed through the fourth wall"); + + args.Handled = true; } } diff --git a/Content.Server/LandMines/LandMineComponent.cs b/Content.Server/LandMines/LandMineComponent.cs index 2d2024ee63..459d9ac1f6 100644 --- a/Content.Server/LandMines/LandMineComponent.cs +++ b/Content.Server/LandMines/LandMineComponent.cs @@ -3,5 +3,6 @@ [RegisterComponent] public sealed class LandMineComponent : Component { - + [DataField("deleteOnActivate")] + public bool DeleteOnActivate = true; } diff --git a/Content.Server/LandMines/LandMineSystem.cs b/Content.Server/LandMines/LandMineSystem.cs index c531ba265e..a19ad44775 100644 --- a/Content.Server/LandMines/LandMineSystem.cs +++ b/Content.Server/LandMines/LandMineSystem.cs @@ -27,14 +27,16 @@ public sealed class LandMineSystem : EntitySystem private void HandleTriggered(EntityUid uid, LandMineComponent component, ref StepTriggeredEvent args) { - _popupSystem.PopupCoordinates( - Loc.GetString("land-mine-triggered", ("mine", uid)), - Transform(uid).Coordinates, - Filter.Entities(args.Tripper)); + if (_trigger.Trigger(uid, args.Tripper)) + { + _popupSystem.PopupCoordinates( + Loc.GetString("land-mine-triggered", ("mine", uid)), + Transform(uid).Coordinates, + Filter.Entities(args.Tripper)); + } - _trigger.Trigger(uid, args.Tripper); - - QueueDel(uid); + if (component.DeleteOnActivate) + QueueDel(uid); } } diff --git a/Content.Server/Payload/EntitySystems/PayloadSystem.cs b/Content.Server/Payload/EntitySystems/PayloadSystem.cs index 870bec9c8c..3b92dc8e18 100644 --- a/Content.Server/Payload/EntitySystems/PayloadSystem.cs +++ b/Content.Server/Payload/EntitySystems/PayloadSystem.cs @@ -1,8 +1,10 @@ +using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Chemistry.EntitySystems; using Content.Server.Explosion.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Database; +using Content.Shared.Examine; using Content.Shared.Payload.Components; using Content.Shared.Tag; using Robust.Shared.Containers; @@ -27,22 +29,34 @@ public sealed class PayloadSystem : EntitySystem SubscribeLocalEvent(OnTriggerTriggered); SubscribeLocalEvent(OnEntityInserted); SubscribeLocalEvent(OnEntityRemoved); + SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(HandleChemicalPayloadTrigger); } + public IEnumerable GetAllPayloads(EntityUid uid, ContainerManagerComponent? contMan=null) + { + if (!Resolve(uid, ref contMan, false)) + yield break; + + foreach (var container in contMan.Containers.Values) + { + foreach (var entity in container.ContainedEntities) + { + if (_tagSystem.HasTag(entity, "Payload")) + yield return entity; + } + } + } + private void OnCaseTriggered(EntityUid uid, PayloadCaseComponent component, TriggerEvent args) { if (!TryComp(uid, out ContainerManagerComponent? contMan)) return; // Pass trigger event onto all contained payloads. Payload capacity configurable by construction graphs. - foreach (var container in contMan.Containers.Values) + foreach (var ent in GetAllPayloads(uid, contMan)) { - foreach (var entity in container.ContainedEntities) - { - if (_tagSystem.HasTag(entity, "Payload")) - RaiseLocalEvent(entity, args, false); - } + RaiseLocalEvent(ent, args, false); } } @@ -106,6 +120,24 @@ public sealed class PayloadSystem : EntitySystem trigger.GrantedComponents.Clear(); } + private void OnExamined(EntityUid uid, PayloadCaseComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange) + { + args.PushMarkup(Loc.GetString("payload-case-not-close-enough", ("ent", uid))); + return; + } + + if (GetAllPayloads(uid).Any()) + { + args.PushMarkup(Loc.GetString("payload-case-has-payload", ("ent", uid))); + } + else + { + args.PushMarkup(Loc.GetString("payload-case-does-not-have-payload", ("ent", uid))); + } + } + private void HandleChemicalPayloadTrigger(EntityUid uid, ChemicalPayloadComponent component, TriggerEvent args) { if (component.BeakerSlotA.Item is not EntityUid beakerA @@ -135,5 +167,7 @@ public sealed class PayloadSystem : EntitySystem _solutionSystem.TryAddSolution(beakerB, solutionB, tmpSol); solutionA.MaxVolume -= solutionB.MaxVolume; _solutionSystem.UpdateChemicals(beakerA, solutionA, false); + + args.Handled = true; } } diff --git a/Content.Server/Sound/EmitSoundSystem.cs b/Content.Server/Sound/EmitSoundSystem.cs index 816fd70880..7a9461b736 100644 --- a/Content.Server/Sound/EmitSoundSystem.cs +++ b/Content.Server/Sound/EmitSoundSystem.cs @@ -40,6 +40,7 @@ namespace Content.Server.Sound private void HandleEmitSoundOnTrigger(EntityUid uid, EmitSoundOnTriggerComponent component, TriggerEvent args) { TryEmitSound(component); + args.Handled = true; } private void HandleEmitSoundOnLand(EntityUid eUI, BaseEmitSoundComponent component, LandEvent arg) diff --git a/Resources/Locale/en-US/payload/payload-case.ftl b/Resources/Locale/en-US/payload/payload-case.ftl new file mode 100644 index 0000000000..d7c567e9cc --- /dev/null +++ b/Resources/Locale/en-US/payload/payload-case.ftl @@ -0,0 +1,3 @@ +payload-case-not-close-enough = You need to get closer to determine if {THE($ent)} has a payload installed. +payload-case-has-payload = {CAPITALIZE(THE($ent))} has a payload installed! +payload-case-does-not-have-payload = {CAPITALIZE(THE($ent))} does not have a payload installed. diff --git a/Resources/Prototypes/Entities/Objects/Misc/land_mine.yml b/Resources/Prototypes/Entities/Objects/Misc/land_mine.yml index 7fe2ca851a..d9d53b18c6 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/land_mine.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/land_mine.yml @@ -4,6 +4,8 @@ components: - type: Clickable - type: InteractionOutline + - type: Anchorable + - type: Pullable - type: MovedByPressure - type: Physics bodyType: Static @@ -18,9 +20,20 @@ layer: - LowImpassable - type: Sprite - drawdepth: FloorObjects + drawdepth: Items sprite: Objects/Misc/uglymine.rsi state: uglymine + - type: Damageable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:TriggerBehavior + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: LandMine - type: StepTrigger requiredTriggeredSpeed: 0 @@ -32,6 +45,19 @@ components: - type: GhostKickUserOnTrigger +- type: entity + name: modular mine + description: This bad boy could be packing any number of dangers. Or a bike horn. + parent: BaseLandMine + id: LandMineModular + components: + - type: PayloadCase + - type: Construction + graph: ModularMineGraph + node: emptyCase + - type: LandMine + deleteOnActivate: false + - type: entity name: explosive mine parent: BaseLandMine diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml index 7d667afd0c..748f1d2518 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml @@ -1,3 +1,5 @@ +# TODO probably needs a base grenade + - type: entity name: explosive grenade description: Grenade that creates a small but devastating explosion. @@ -32,6 +34,7 @@ !type:DamageTrigger damage: 10 behaviors: + - !type:TriggerBehavior - !type:DoActsBehavior acts: ["Destruction"] - type: Appearance @@ -71,6 +74,7 @@ !type:DamageTrigger damage: 10 behaviors: + - !type:TriggerBehavior - !type:DoActsBehavior acts: ["Destruction"] - type: Appearance @@ -108,6 +112,7 @@ !type:DamageTrigger damage: 10 behaviors: + - !type:TriggerBehavior - !type:DoActsBehavior acts: ["Destruction"] - type: Appearance @@ -115,6 +120,7 @@ - type: TimerTriggerVisualizer countdown_sound: path: /Audio/Effects/minibombcountdown.ogg + - type: entity name: the nuclear option description: Please don't throw it, think of the children. @@ -144,6 +150,7 @@ !type:DamageTrigger damage: 50 behaviors: + - !type:TriggerBehavior - !type:DoActsBehavior acts: ["Destruction"] - type: Appearance @@ -175,6 +182,7 @@ !type:DamageTrigger damage: 50 behaviors: + - !type:TriggerBehavior - !type:DoActsBehavior acts: [ "Destruction" ] - type: Appearance diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_mine.yml b/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_mine.yml new file mode 100644 index 0000000000..afb90f4d02 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/weapons/modular_mine.yml @@ -0,0 +1,55 @@ +- type: constructionGraph + id: ModularMineGraph + start: start + graph: + + - node: start + edges: + - to: emptyCase + steps: + - material: Steel + amount: 5 + doAfter: 1 + + - node: emptyCase + entity: LandMineModular + edges: + - to: wiredCase + steps: + - material: Cable + doAfter: 0.5 + + - node: wiredCase + entity: LandMineModular + actions: + - !type:PlaySound + sound: /Audio/Machines/button.ogg + edges: + - to: emptyCase + steps: + - tool: Cutting + doAfter: 0.5 + completed: + - !type:SpawnPrototype + prototype: CableApcStack1 + - to: mine + steps: + - tag: Payload + store: payload + name: Payload + doAfter: 0.5 + + - node: mine + actions: + - !type:PlaySound + sound: /Audio/Machines/button.ogg + - !type:AdminLog + message: "A mine was crafted" + edges: + - to: wiredCase + steps: + - tool: Prying + doAfter: 0.5 + completed: + - !type:EmptyContainer + container: payload diff --git a/Resources/Prototypes/Recipes/Construction/modular.yml b/Resources/Prototypes/Recipes/Construction/modular.yml new file mode 100644 index 0000000000..8ad8098476 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/modular.yml @@ -0,0 +1,25 @@ +- type: construction + name: Modular Grenade + id: ModularGrenadeRecipe + graph: ModularGrenadeGraph + startNode: start + targetNode: grenade + category: Weapons + description: Construct a grenade using a trigger and a payload. + icon: + sprite: Objects/Weapons/Grenades/modular.rsi + state: complete + objectType: Item + +- type: construction + name: Modular Mine + id: ModularMineRecipe + graph: ModularMineGraph + startNode: start + targetNode: mine + category: Weapons + description: Construct a landmine using a payload. + icon: + sprite: Objects/Misc/uglymine.rsi + state: uglymine + objectType: Item diff --git a/Resources/Prototypes/Recipes/Construction/modular_grenades.yml b/Resources/Prototypes/Recipes/Construction/modular_grenades.yml deleted file mode 100644 index 280c8a0ad7..0000000000 --- a/Resources/Prototypes/Recipes/Construction/modular_grenades.yml +++ /dev/null @@ -1,12 +0,0 @@ -- type: construction - name: Modular Grenade - id: ModularGrenadeRecipe - graph: ModularGrenadeGraph - startNode: start - targetNode: grenade - category: Weapons - description: Construct a grenade using a trigger and a payload. - icon: - sprite: Objects/Weapons/Grenades/modular.rsi - state: complete - objectType: Item \ No newline at end of file