TriggerSystem improvements (#35762)

* desynchronizer real

* yaml stuff from slarti branch

* C# stuff

* oops

* fix triggers

* atomize PR

---------

Co-authored-by: Flareguy <woaj9999@outlook.com>
This commit is contained in:
slarticodefast
2025-03-12 06:31:33 +01:00
committed by GitHub
parent a384cbed89
commit 175f5e6c2f
11 changed files with 201 additions and 98 deletions

View File

@@ -1,12 +1,24 @@
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Explosion.Components; namespace Content.Server.Explosion.Components;
/// <summary>
/// Spawns a protoype when triggered.
/// </summary>
[RegisterComponent, Access(typeof(TriggerSystem))] [RegisterComponent, Access(typeof(TriggerSystem))]
public sealed partial class SpawnOnTriggerComponent : Component public sealed partial class SpawnOnTriggerComponent : Component
{ {
[ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))] /// <summary>
public string Proto = string.Empty; /// The prototype to spawn.
/// </summary>
[DataField(required: true)]
public EntProtoId Proto = string.Empty;
/// <summary>
/// Use MapCoordinates for spawning?
/// Set to true if you don't want the new entity parented to the spawner.
/// </summary>
[DataField]
public bool mapCoords;
} }

View File

@@ -1,15 +1,20 @@
namespace Content.Server.Explosion.Components namespace Content.Server.Explosion.Components;
{
[RegisterComponent]
public sealed partial class TriggerOnCollideComponent : Component
{
[DataField("fixtureID", required: true)]
public string FixtureID = String.Empty;
/// <summary> /// <summary>
/// Doesn't trigger if the other colliding fixture is nonhard. /// Triggers when colliding with another entity.
/// </summary> /// </summary>
[DataField("ignoreOtherNonHard")] [RegisterComponent]
public bool IgnoreOtherNonHard = true; public sealed partial class TriggerOnCollideComponent : Component
} {
/// <summary>
/// The fixture with which to collide.
/// </summary>
[DataField(required: true)]
public string FixtureID = string.Empty;
/// <summary>
/// Doesn't trigger if the other colliding fixture is nonhard.
/// </summary>
[DataField]
public bool IgnoreOtherNonHard = true;
} }

View File

@@ -0,0 +1,7 @@
namespace Content.Server.Explosion.Components;
/// <summary>
/// Triggers on use in hand.
/// </summary>
[RegisterComponent]
public sealed partial class TriggerOnUseComponent : Component { }

View File

@@ -0,0 +1,23 @@
using Content.Shared.Whitelist;
namespace Content.Server.Explosion.Components;
/// <summary>
/// Checks if the user of a Trigger satisfies a whitelist and blacklist condition.
/// Cancels the trigger otherwise.
/// </summary>
[RegisterComponent]
public sealed partial class TriggerWhitelistComponent : Component
{
/// <summary>
/// Whitelist for what entites can cause this trigger.
/// </summary>
[DataField]
public EntityWhitelist? Whitelist;
/// <summary>
/// Blacklist for what entites can cause this trigger.
/// </summary>
[DataField]
public EntityWhitelist? Blacklist;
}

View File

@@ -14,6 +14,7 @@ using Content.Shared.Explosion.Components;
using Content.Shared.Explosion.Components.OnTrigger; using Content.Shared.Explosion.Components.OnTrigger;
using Content.Shared.Implants.Components; using Content.Shared.Implants.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Mobs; using Content.Shared.Mobs;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
@@ -23,6 +24,7 @@ using Content.Shared.Slippery;
using Content.Shared.StepTrigger.Systems; using Content.Shared.StepTrigger.Systems;
using Content.Shared.Trigger; using Content.Shared.Trigger;
using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Whitelist;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
@@ -31,10 +33,7 @@ using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Player;
using Content.Shared.Coordinates;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Robust.Shared.Timing;
namespace Content.Server.Explosion.EntitySystems namespace Content.Server.Explosion.EntitySystems
{ {
@@ -53,6 +52,12 @@ namespace Content.Server.Explosion.EntitySystems
} }
} }
/// <summary>
/// Raised before a trigger is activated.
/// </summary>
[ByRefEvent]
public record struct BeforeTriggerEvent(EntityUid Triggered, EntityUid? User, bool Cancelled = false);
/// <summary> /// <summary>
/// Raised when timer trigger becomes active. /// Raised when timer trigger becomes active.
/// </summary> /// </summary>
@@ -78,6 +83,7 @@ namespace Content.Server.Explosion.EntitySystems
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly ElectrocutionSystem _electrocution = default!; [Dependency] private readonly ElectrocutionSystem _electrocution = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -93,6 +99,7 @@ namespace Content.Server.Explosion.EntitySystems
SubscribeLocalEvent<TriggerOnSpawnComponent, MapInitEvent>(OnSpawnTriggered); SubscribeLocalEvent<TriggerOnSpawnComponent, MapInitEvent>(OnSpawnTriggered);
SubscribeLocalEvent<TriggerOnCollideComponent, StartCollideEvent>(OnTriggerCollide); SubscribeLocalEvent<TriggerOnCollideComponent, StartCollideEvent>(OnTriggerCollide);
SubscribeLocalEvent<TriggerOnActivateComponent, ActivateInWorldEvent>(OnActivate); SubscribeLocalEvent<TriggerOnActivateComponent, ActivateInWorldEvent>(OnActivate);
SubscribeLocalEvent<TriggerOnUseComponent, UseInHandEvent>(OnUse);
SubscribeLocalEvent<TriggerImplantActionComponent, ActivateImplantEvent>(OnImplantTrigger); SubscribeLocalEvent<TriggerImplantActionComponent, ActivateImplantEvent>(OnImplantTrigger);
SubscribeLocalEvent<TriggerOnStepTriggerComponent, StepTriggeredOffEvent>(OnStepTriggered); SubscribeLocalEvent<TriggerOnStepTriggerComponent, StepTriggeredOffEvent>(OnStepTriggered);
SubscribeLocalEvent<TriggerOnSlipComponent, SlipEvent>(OnSlipTriggered); SubscribeLocalEvent<TriggerOnSlipComponent, SlipEvent>(OnSlipTriggered);
@@ -109,6 +116,13 @@ namespace Content.Server.Explosion.EntitySystems
SubscribeLocalEvent<SoundOnTriggerComponent, TriggerEvent>(OnSoundTrigger); SubscribeLocalEvent<SoundOnTriggerComponent, TriggerEvent>(OnSoundTrigger);
SubscribeLocalEvent<ShockOnTriggerComponent, TriggerEvent>(HandleShockTrigger); SubscribeLocalEvent<ShockOnTriggerComponent, TriggerEvent>(HandleShockTrigger);
SubscribeLocalEvent<RattleComponent, TriggerEvent>(HandleRattleTrigger); SubscribeLocalEvent<RattleComponent, TriggerEvent>(HandleRattleTrigger);
SubscribeLocalEvent<TriggerWhitelistComponent, BeforeTriggerEvent>(HandleWhitelist);
}
private void HandleWhitelist(Entity<TriggerWhitelistComponent> 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) private void OnSoundTrigger(EntityUid uid, SoundOnTriggerComponent component, TriggerEvent args)
@@ -155,16 +169,23 @@ namespace Content.Server.Explosion.EntitySystems
RemCompDeferred<AnchorOnTriggerComponent>(uid); RemCompDeferred<AnchorOnTriggerComponent>(uid);
} }
private void OnSpawnTrigger(EntityUid uid, SpawnOnTriggerComponent component, TriggerEvent args) private void OnSpawnTrigger(Entity<SpawnOnTriggerComponent> 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) private void HandleExplodeTrigger(EntityUid uid, ExplodeOnTriggerComponent component, TriggerEvent args)
@@ -248,6 +269,15 @@ namespace Content.Server.Explosion.EntitySystems
args.Handled = true; args.Handled = true;
} }
private void OnUse(Entity<TriggerOnUseComponent> 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) private void OnImplantTrigger(EntityUid uid, TriggerImplantActionComponent component, ActivateImplantEvent args)
{ {
args.Handled = Trigger(uid); args.Handled = Trigger(uid);
@@ -275,6 +305,11 @@ namespace Content.Server.Explosion.EntitySystems
public bool Trigger(EntityUid trigger, EntityUid? user = null) 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); var triggerEvent = new TriggerEvent(trigger, user);
EntityManager.EventBus.RaiseLocalEvent(trigger, triggerEvent, true); EntityManager.EventBus.RaiseLocalEvent(trigger, triggerEvent, true);
return triggerEvent.Handled; return triggerEvent.Handled;

View File

@@ -0,0 +1,18 @@
using Content.Shared.Polymorph;
using Robust.Shared.Prototypes;
namespace Content.Server.Polymorph.Components;
/// <summary>
/// Intended for use with the trigger system.
/// Polymorphs the user of the trigger.
/// </summary>
[RegisterComponent]
public sealed partial class PolymorphOnTriggerComponent : Component
{
/// <summary>
/// Polymorph settings.
/// </summary>
[DataField(required: true)]
public ProtoId<PolymorphPrototype> Polymorph;
}

View File

@@ -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!;
/// <summary>
/// Need to do this so we don't get a collection enumeration error in physics by polymorphing
/// an entity we're colliding with
/// </summary>
private Queue<PolymorphQueuedData> _queuedPolymorphUpdates = new();
private void InitializeCollide()
{
SubscribeLocalEvent<PolymorphOnCollideComponent, StartCollideEvent>(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<PolymorphPrototype> Polymorph;
public PolymorphQueuedData(EntityUid ent, SoundSpecifier sound, ProtoId<PolymorphPrototype> polymorph)
{
Ent = ent;
Sound = sound;
Polymorph = polymorph;
}
}

View File

@@ -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
{
/// <summary>
/// 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.
/// </summary>
private Queue<(EntityUid Ent, ProtoId<PolymorphPrototype> Polymorph)> _queuedPolymorphUpdates = new();
private void InitializeTrigger()
{
SubscribeLocalEvent<PolymorphOnTriggerComponent, TriggerEvent>(OnTrigger);
}
private void OnTrigger(Entity<PolymorphOnTriggerComponent> 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);
}
}
}

View File

@@ -60,8 +60,8 @@ public sealed partial class PolymorphSystem : EntitySystem
SubscribeLocalEvent<PolymorphedEntityComponent, BeforeFullySlicedEvent>(OnBeforeFullySliced); SubscribeLocalEvent<PolymorphedEntityComponent, BeforeFullySlicedEvent>(OnBeforeFullySliced);
SubscribeLocalEvent<PolymorphedEntityComponent, DestructionEventArgs>(OnDestruction); SubscribeLocalEvent<PolymorphedEntityComponent, DestructionEventArgs>(OnDestruction);
InitializeCollide();
InitializeMap(); InitializeMap();
InitializeTrigger();
} }
public override void Update(float frameTime) public override void Update(float frameTime)
@@ -89,7 +89,7 @@ public sealed partial class PolymorphSystem : EntitySystem
} }
} }
UpdateCollide(); UpdateTrigger();
} }
private void OnComponentStartup(Entity<PolymorphableComponent> ent, ref ComponentStartup args) private void OnComponentStartup(Entity<PolymorphableComponent> ent, ref ComponentStartup args)

View File

@@ -173,6 +173,8 @@
damage: damage:
types: types:
Poison: 5 Poison: 5
- type: TriggerOnCollide
fixtureID: projectile
- type: entity - type: entity
id: ProjectilePolyboltCarp id: ProjectilePolyboltCarp
@@ -181,8 +183,9 @@
description: Nooo, I don't wanna be fish! description: Nooo, I don't wanna be fish!
categories: [ HideSpawnMenu ] categories: [ HideSpawnMenu ]
components: components:
- type: PolymorphOnCollide - type: PolymorphOnTrigger
polymorph: WizardForcedCarp polymorph: WizardForcedCarp
- type: TriggerWhitelist
whitelist: whitelist:
components: components:
- Body - Body
@@ -194,8 +197,9 @@
description: Nooo, I don't wanna be monkey! description: Nooo, I don't wanna be monkey!
categories: [ HideSpawnMenu ] categories: [ HideSpawnMenu ]
components: components:
- type: PolymorphOnCollide - type: PolymorphOnTrigger
polymorph: WizardForcedMonkey polymorph: WizardForcedMonkey
- type: TriggerWhitelist
whitelist: whitelist:
components: components:
- Body - Body
@@ -212,8 +216,9 @@
layers: layers:
- state: spell - state: spell
color: brown color: brown
- type: PolymorphOnCollide - type: PolymorphOnTrigger
polymorph: WizardWallDoor polymorph: WizardWallDoor
- type: TriggerWhitelist
whitelist: whitelist:
components: components:
- Airlock - Airlock
@@ -262,8 +267,9 @@
description: KnoH KnoH! description: KnoH KnoH!
categories: [ HideSpawnMenu ] categories: [ HideSpawnMenu ]
components: components:
- type: PolymorphOnCollide - type: PolymorphOnTrigger
polymorph: WizardForcedCluwne polymorph: WizardForcedCluwne
- type: TriggerWhitelist
whitelist: whitelist:
components: components:
- Body - Body
@@ -291,8 +297,9 @@
description: Nooo, I don't wanna be bread! description: Nooo, I don't wanna be bread!
categories: [ HideSpawnMenu ] categories: [ HideSpawnMenu ]
components: components:
- type: PolymorphOnCollide - type: PolymorphOnTrigger
polymorph: BreadMorph polymorph: BreadMorph
- type: TriggerWhitelist
whitelist: whitelist:
components: components:
- Body - Body

View File

@@ -31,6 +31,10 @@
transferDamage: true transferDamage: true
revertOnCrit: false revertOnCrit: false
revertOnDeath: true revertOnDeath: true
polymorphSound: !type:SoundPathSpecifier
path: /Audio/Magic/forcewall.ogg
exitPolymorphSound: !type:SoundPathSpecifier
path: /Audio/Magic/forcewall.ogg
- type: polymorph - type: polymorph
id: WizardForcedSkeleton id: WizardForcedSkeleton
@@ -42,6 +46,10 @@
transferDamage: true transferDamage: true
revertOnCrit: false revertOnCrit: false
revertOnDeath: false revertOnDeath: false
polymorphSound: !type:SoundPathSpecifier
path: /Audio/Magic/forcewall.ogg
exitPolymorphSound: !type:SoundPathSpecifier
path: /Audio/Magic/forcewall.ogg
- type: polymorph - type: polymorph
id: WizardForcedMonkey id: WizardForcedMonkey
@@ -53,6 +61,10 @@
transferDamage: true transferDamage: true
revertOnCrit: false revertOnCrit: false
revertOnDeath: true revertOnDeath: true
polymorphSound: !type:SoundPathSpecifier
path: /Audio/Magic/forcewall.ogg
exitPolymorphSound: !type:SoundPathSpecifier
path: /Audio/Magic/forcewall.ogg
- type: polymorph - type: polymorph
id: WizardWallDoor id: WizardWallDoor
@@ -64,6 +76,10 @@
transferDamage: false transferDamage: false
revertOnCrit: false revertOnCrit: false
revertOnDeath: false revertOnDeath: false
polymorphSound: !type:SoundPathSpecifier
path: /Audio/Magic/forcewall.ogg
exitPolymorphSound: !type:SoundPathSpecifier
path: /Audio/Magic/forcewall.ogg
- type: polymorph - type: polymorph
id: WizardForcedCluwne id: WizardForcedCluwne
@@ -74,6 +90,10 @@
transferHumanoidAppearance: true transferHumanoidAppearance: true
inventory: Transfer inventory: Transfer
revertOnDeath: true 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 # this is a test for transferring some visual appearance stuff
- type: polymorph - type: polymorph