diff --git a/Content.Server/Explosion/Components/SpawnOnTriggerComponent.cs b/Content.Server/Explosion/Components/SpawnOnTriggerComponent.cs
index a8b36fbd84..c28ec7faeb 100644
--- a/Content.Server/Explosion/Components/SpawnOnTriggerComponent.cs
+++ b/Content.Server/Explosion/Components/SpawnOnTriggerComponent.cs
@@ -1,12 +1,24 @@
using Content.Server.Explosion.EntitySystems;
using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Explosion.Components;
+///
+/// Spawns a protoype when triggered.
+///
[RegisterComponent, Access(typeof(TriggerSystem))]
public sealed partial class SpawnOnTriggerComponent : Component
{
- [ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))]
- public string Proto = string.Empty;
+ ///
+ /// The prototype to spawn.
+ ///
+ [DataField(required: true)]
+ public EntProtoId Proto = string.Empty;
+
+ ///
+ /// Use MapCoordinates for spawning?
+ /// Set to true if you don't want the new entity parented to the spawner.
+ ///
+ [DataField]
+ public bool mapCoords;
}
diff --git a/Content.Server/Explosion/Components/TriggerOnCollideComponent.cs b/Content.Server/Explosion/Components/TriggerOnCollideComponent.cs
index 950bd3e462..28c2ed8c34 100644
--- a/Content.Server/Explosion/Components/TriggerOnCollideComponent.cs
+++ b/Content.Server/Explosion/Components/TriggerOnCollideComponent.cs
@@ -1,15 +1,20 @@
-namespace Content.Server.Explosion.Components
-{
- [RegisterComponent]
- public sealed partial class TriggerOnCollideComponent : Component
- {
- [DataField("fixtureID", required: true)]
- public string FixtureID = String.Empty;
+namespace Content.Server.Explosion.Components;
- ///
- /// Doesn't trigger if the other colliding fixture is nonhard.
- ///
- [DataField("ignoreOtherNonHard")]
- public bool IgnoreOtherNonHard = true;
- }
+///
+/// Triggers when colliding with another entity.
+///
+[RegisterComponent]
+public sealed partial class TriggerOnCollideComponent : Component
+{
+ ///
+ /// The fixture with which to collide.
+ ///
+ [DataField(required: true)]
+ public string FixtureID = string.Empty;
+
+ ///
+ /// Doesn't trigger if the other colliding fixture is nonhard.
+ ///
+ [DataField]
+ public bool IgnoreOtherNonHard = true;
}
diff --git a/Content.Server/Explosion/Components/TriggerOnUseComponent.cs b/Content.Server/Explosion/Components/TriggerOnUseComponent.cs
new file mode 100644
index 0000000000..2b44d2fbac
--- /dev/null
+++ b/Content.Server/Explosion/Components/TriggerOnUseComponent.cs
@@ -0,0 +1,7 @@
+namespace Content.Server.Explosion.Components;
+
+///
+/// Triggers on use in hand.
+///
+[RegisterComponent]
+public sealed partial class TriggerOnUseComponent : Component { }
diff --git a/Content.Server/Explosion/Components/TriggerWhitelistComponent.cs b/Content.Server/Explosion/Components/TriggerWhitelistComponent.cs
new file mode 100644
index 0000000000..80becf17cc
--- /dev/null
+++ b/Content.Server/Explosion/Components/TriggerWhitelistComponent.cs
@@ -0,0 +1,23 @@
+using Content.Shared.Whitelist;
+
+namespace Content.Server.Explosion.Components;
+
+///
+/// Checks if the user of a Trigger satisfies a whitelist and blacklist condition.
+/// Cancels the trigger otherwise.
+///
+[RegisterComponent]
+public sealed partial class TriggerWhitelistComponent : Component
+{
+ ///
+ /// Whitelist for what entites can cause this trigger.
+ ///
+ [DataField]
+ public EntityWhitelist? Whitelist;
+
+ ///
+ /// Blacklist for what entites can cause this trigger.
+ ///
+ [DataField]
+ public EntityWhitelist? Blacklist;
+}
diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
index 53f6dfacf8..0459730c64 100644
--- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
@@ -14,6 +14,7 @@ using Content.Shared.Explosion.Components;
using Content.Shared.Explosion.Components.OnTrigger;
using Content.Shared.Implants.Components;
using Content.Shared.Interaction;
+using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
@@ -23,6 +24,7 @@ using Content.Shared.Slippery;
using Content.Shared.StepTrigger.Systems;
using Content.Shared.Trigger;
using Content.Shared.Weapons.Ranged.Events;
+using Content.Shared.Whitelist;
using JetBrains.Annotations;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
@@ -31,10 +33,7 @@ using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
-using Robust.Shared.Player;
-using Content.Shared.Coordinates;
using Robust.Shared.Utility;
-using Robust.Shared.Timing;
namespace Content.Server.Explosion.EntitySystems
{
@@ -53,6 +52,12 @@ namespace Content.Server.Explosion.EntitySystems
}
}
+ ///
+ /// Raised before a trigger is activated.
+ ///
+ [ByRefEvent]
+ public record struct BeforeTriggerEvent(EntityUid Triggered, EntityUid? User, bool Cancelled = false);
+
///
/// Raised when timer trigger becomes active.
///
@@ -78,6 +83,7 @@ namespace Content.Server.Explosion.EntitySystems
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly ElectrocutionSystem _electrocution = default!;
+ [Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
public override void Initialize()
{
@@ -93,6 +99,7 @@ namespace Content.Server.Explosion.EntitySystems
SubscribeLocalEvent(OnSpawnTriggered);
SubscribeLocalEvent(OnTriggerCollide);
SubscribeLocalEvent(OnActivate);
+ SubscribeLocalEvent(OnUse);
SubscribeLocalEvent(OnImplantTrigger);
SubscribeLocalEvent(OnStepTriggered);
SubscribeLocalEvent(OnSlipTriggered);
@@ -109,6 +116,13 @@ namespace Content.Server.Explosion.EntitySystems
SubscribeLocalEvent(OnSoundTrigger);
SubscribeLocalEvent(HandleShockTrigger);
SubscribeLocalEvent(HandleRattleTrigger);
+
+ SubscribeLocalEvent(HandleWhitelist);
+ }
+
+ private void HandleWhitelist(Entity ent, ref BeforeTriggerEvent args)
+ {
+ args.Cancelled = !_whitelist.CheckBoth(args.User, ent.Comp.Blacklist, ent.Comp.Whitelist);
}
private void OnSoundTrigger(EntityUid uid, SoundOnTriggerComponent component, TriggerEvent args)
@@ -155,16 +169,23 @@ namespace Content.Server.Explosion.EntitySystems
RemCompDeferred(uid);
}
- private void OnSpawnTrigger(EntityUid uid, SpawnOnTriggerComponent component, TriggerEvent args)
+ private void OnSpawnTrigger(Entity ent, ref TriggerEvent args)
{
- var xform = Transform(uid);
+ var xform = Transform(ent);
- var coords = xform.Coordinates;
+ if (ent.Comp.mapCoords)
+ {
+ var mapCoords = _transformSystem.GetMapCoordinates(ent, xform);
+ Spawn(ent.Comp.Proto, mapCoords);
+ }
+ else
+ {
+ var coords = xform.Coordinates;
+ if (!coords.IsValid(EntityManager))
+ return;
+ Spawn(ent.Comp.Proto, coords);
- if (!coords.IsValid(EntityManager))
- return;
-
- Spawn(component.Proto, coords);
+ }
}
private void HandleExplodeTrigger(EntityUid uid, ExplodeOnTriggerComponent component, TriggerEvent args)
@@ -248,6 +269,15 @@ namespace Content.Server.Explosion.EntitySystems
args.Handled = true;
}
+ private void OnUse(Entity ent, ref UseInHandEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ Trigger(ent.Owner, args.User);
+ args.Handled = true;
+ }
+
private void OnImplantTrigger(EntityUid uid, TriggerImplantActionComponent component, ActivateImplantEvent args)
{
args.Handled = Trigger(uid);
@@ -275,6 +305,11 @@ namespace Content.Server.Explosion.EntitySystems
public bool Trigger(EntityUid trigger, EntityUid? user = null)
{
+ var beforeTriggerEvent = new BeforeTriggerEvent(trigger, user);
+ RaiseLocalEvent(trigger, ref beforeTriggerEvent);
+ if (beforeTriggerEvent.Cancelled)
+ return false;
+
var triggerEvent = new TriggerEvent(trigger, user);
EntityManager.EventBus.RaiseLocalEvent(trigger, triggerEvent, true);
return triggerEvent.Handled;
diff --git a/Content.Server/Polymorph/Components/PolymorphOnTriggerComponent.cs b/Content.Server/Polymorph/Components/PolymorphOnTriggerComponent.cs
new file mode 100644
index 0000000000..a11b4f4d4c
--- /dev/null
+++ b/Content.Server/Polymorph/Components/PolymorphOnTriggerComponent.cs
@@ -0,0 +1,18 @@
+using Content.Shared.Polymorph;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Polymorph.Components;
+
+///
+/// Intended for use with the trigger system.
+/// Polymorphs the user of the trigger.
+///
+[RegisterComponent]
+public sealed partial class PolymorphOnTriggerComponent : Component
+{
+ ///
+ /// Polymorph settings.
+ ///
+ [DataField(required: true)]
+ public ProtoId Polymorph;
+}
diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs
deleted file mode 100644
index b29f46e09e..0000000000
--- a/Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using Content.Server.Polymorph.Components;
-using Content.Shared.Polymorph;
-using Content.Shared.Projectiles;
-using Content.Shared.Whitelist;
-using Robust.Shared.Audio;
-using Robust.Shared.Physics.Events;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Polymorph.Systems;
-
-public partial class PolymorphSystem
-{
- [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
-
- ///
- /// Need to do this so we don't get a collection enumeration error in physics by polymorphing
- /// an entity we're colliding with
- ///
- private Queue _queuedPolymorphUpdates = new();
-
- private void InitializeCollide()
- {
- SubscribeLocalEvent(OnPolymorphCollide);
- }
-
- public void UpdateCollide()
- {
- while (_queuedPolymorphUpdates.TryDequeue(out var data))
- {
- if (Deleted(data.Ent))
- continue;
-
- var ent = PolymorphEntity(data.Ent, data.Polymorph);
- if (ent != null)
- _audio.PlayPvs(data.Sound, ent.Value);
- }
- }
-
- private void OnPolymorphCollide(EntityUid uid, PolymorphOnCollideComponent component, ref StartCollideEvent args)
- {
- if (args.OurFixtureId != SharedProjectileSystem.ProjectileFixture)
- return;
-
- var other = args.OtherEntity;
- if (_whitelistSystem.IsWhitelistFail(component.Whitelist, other) ||
- _whitelistSystem.IsBlacklistPass(component.Blacklist, other))
- return;
-
- _queuedPolymorphUpdates.Enqueue(new (other, component.Sound, component.Polymorph));
- }
-}
-
-public struct PolymorphQueuedData
-{
- public EntityUid Ent;
- public SoundSpecifier Sound;
- public ProtoId Polymorph;
-
- public PolymorphQueuedData(EntityUid ent, SoundSpecifier sound, ProtoId polymorph)
- {
- Ent = ent;
- Sound = sound;
- Polymorph = polymorph;
- }
-}
diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.Trigger.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.Trigger.cs
new file mode 100644
index 0000000000..452b060315
--- /dev/null
+++ b/Content.Server/Polymorph/Systems/PolymorphSystem.Trigger.cs
@@ -0,0 +1,41 @@
+using Content.Shared.Polymorph;
+using Content.Server.Polymorph.Components;
+using Content.Server.Explosion.EntitySystems;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Polymorph.Systems;
+
+public sealed partial class PolymorphSystem
+{
+ ///
+ /// Need to do this so we don't get a collection enumeration error in physics by polymorphing
+ /// an entity we're colliding with in case of TriggerOnCollide.
+ /// Also makes sure other trigger effects don't activate in nullspace after we have polymorphed.
+ ///
+ private Queue<(EntityUid Ent, ProtoId Polymorph)> _queuedPolymorphUpdates = new();
+
+ private void InitializeTrigger()
+ {
+ SubscribeLocalEvent(OnTrigger);
+ }
+
+ private void OnTrigger(Entity ent, ref TriggerEvent args)
+ {
+ if (args.User == null)
+ return;
+
+ _queuedPolymorphUpdates.Enqueue((args.User.Value, ent.Comp.Polymorph));
+ args.Handled = true;
+ }
+
+ public void UpdateTrigger()
+ {
+ while (_queuedPolymorphUpdates.TryDequeue(out var data))
+ {
+ if (TerminatingOrDeleted(data.Item1))
+ continue;
+
+ PolymorphEntity(data.Item1, data.Item2);
+ }
+ }
+}
diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs
index 2c06ba3be9..8adab1e00d 100644
--- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs
+++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs
@@ -60,8 +60,8 @@ public sealed partial class PolymorphSystem : EntitySystem
SubscribeLocalEvent(OnBeforeFullySliced);
SubscribeLocalEvent(OnDestruction);
- InitializeCollide();
InitializeMap();
+ InitializeTrigger();
}
public override void Update(float frameTime)
@@ -89,7 +89,7 @@ public sealed partial class PolymorphSystem : EntitySystem
}
}
- UpdateCollide();
+ UpdateTrigger();
}
private void OnComponentStartup(Entity ent, ref ComponentStartup args)
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
index 4c3fd937e0..c587787d48 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
@@ -173,6 +173,8 @@
damage:
types:
Poison: 5
+ - type: TriggerOnCollide
+ fixtureID: projectile
- type: entity
id: ProjectilePolyboltCarp
@@ -181,8 +183,9 @@
description: Nooo, I don't wanna be fish!
categories: [ HideSpawnMenu ]
components:
- - type: PolymorphOnCollide
+ - type: PolymorphOnTrigger
polymorph: WizardForcedCarp
+ - type: TriggerWhitelist
whitelist:
components:
- Body
@@ -194,8 +197,9 @@
description: Nooo, I don't wanna be monkey!
categories: [ HideSpawnMenu ]
components:
- - type: PolymorphOnCollide
+ - type: PolymorphOnTrigger
polymorph: WizardForcedMonkey
+ - type: TriggerWhitelist
whitelist:
components:
- Body
@@ -212,8 +216,9 @@
layers:
- state: spell
color: brown
- - type: PolymorphOnCollide
+ - type: PolymorphOnTrigger
polymorph: WizardWallDoor
+ - type: TriggerWhitelist
whitelist:
components:
- Airlock
@@ -262,8 +267,9 @@
description: KnoH KnoH!
categories: [ HideSpawnMenu ]
components:
- - type: PolymorphOnCollide
+ - type: PolymorphOnTrigger
polymorph: WizardForcedCluwne
+ - type: TriggerWhitelist
whitelist:
components:
- Body
@@ -291,8 +297,9 @@
description: Nooo, I don't wanna be bread!
categories: [ HideSpawnMenu ]
components:
- - type: PolymorphOnCollide
+ - type: PolymorphOnTrigger
polymorph: BreadMorph
+ - type: TriggerWhitelist
whitelist:
components:
- Body
diff --git a/Resources/Prototypes/Polymorphs/polymorph.yml b/Resources/Prototypes/Polymorphs/polymorph.yml
index 745e032e7d..935b2b27f5 100644
--- a/Resources/Prototypes/Polymorphs/polymorph.yml
+++ b/Resources/Prototypes/Polymorphs/polymorph.yml
@@ -31,6 +31,10 @@
transferDamage: true
revertOnCrit: false
revertOnDeath: true
+ polymorphSound: !type:SoundPathSpecifier
+ path: /Audio/Magic/forcewall.ogg
+ exitPolymorphSound: !type:SoundPathSpecifier
+ path: /Audio/Magic/forcewall.ogg
- type: polymorph
id: WizardForcedSkeleton
@@ -42,6 +46,10 @@
transferDamage: true
revertOnCrit: false
revertOnDeath: false
+ polymorphSound: !type:SoundPathSpecifier
+ path: /Audio/Magic/forcewall.ogg
+ exitPolymorphSound: !type:SoundPathSpecifier
+ path: /Audio/Magic/forcewall.ogg
- type: polymorph
id: WizardForcedMonkey
@@ -53,6 +61,10 @@
transferDamage: true
revertOnCrit: false
revertOnDeath: true
+ polymorphSound: !type:SoundPathSpecifier
+ path: /Audio/Magic/forcewall.ogg
+ exitPolymorphSound: !type:SoundPathSpecifier
+ path: /Audio/Magic/forcewall.ogg
- type: polymorph
id: WizardWallDoor
@@ -64,6 +76,10 @@
transferDamage: false
revertOnCrit: false
revertOnDeath: false
+ polymorphSound: !type:SoundPathSpecifier
+ path: /Audio/Magic/forcewall.ogg
+ exitPolymorphSound: !type:SoundPathSpecifier
+ path: /Audio/Magic/forcewall.ogg
- type: polymorph
id: WizardForcedCluwne
@@ -74,6 +90,10 @@
transferHumanoidAppearance: true
inventory: Transfer
revertOnDeath: true
+ polymorphSound: !type:SoundPathSpecifier
+ path: /Audio/Magic/forcewall.ogg
+ exitPolymorphSound: !type:SoundPathSpecifier
+ path: /Audio/Magic/forcewall.ogg
# this is a test for transferring some visual appearance stuff
- type: polymorph