From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 12 Mar 2025 05:31:33 +0000 (+0100) Subject: TriggerSystem improvements (#35762) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=175f5e6c2fbefc4d810fe9b6c04e22daf9798260;p=space-station-14.git TriggerSystem improvements (#35762) * desynchronizer real * yaml stuff from slarti branch * C# stuff * oops * fix triggers * atomize PR --------- Co-authored-by: Flareguy --- 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 +namespace Content.Server.Explosion.Components; + +/// +/// Triggers when colliding with another entity. +/// +[RegisterComponent] +public sealed partial class TriggerOnCollideComponent : Component { - [RegisterComponent] - public sealed partial class TriggerOnCollideComponent : Component - { - [DataField("fixtureID", required: true)] - public string FixtureID = String.Empty; + /// + /// 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("ignoreOtherNonHard")] - public bool IgnoreOtherNonHard = true; - } + /// + /// 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 (!coords.IsValid(EntityManager)) - return; + 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); - 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