Skip to content

Conversation

slarticodefast
Copy link
Member

@slarticodefast slarticodefast commented Jul 17, 2025

About the PR

This PR got soaped, but it was worth it.
It should make the trigger system much more powerful and flexible to use:

  • Almost all triggers and trigger effects are now predicted
  • All trigger effects can now either target the user or the triggered entity, selectable via a bool DataField.
  • You can have multiple independent triggers on the same entity.
  • Conditions like usedelays or whitelists were dehardcoded from some trigger effects, moved into separate components and can now be used with any trigger.

Why / Balance

The trigger system is really powerful, but underused because it was missing flexibility and prediction. The refactor allows us to replace a lot of components that have been hardcoded for very specific purposes with small reusable building blocks.
For example
ExtinguishOnInteract would become ExtinguishOnTrigger and TriggerOnInteract, both of which could be combined with any other trigger.
Just to name a few other components that could be replaced with this:
AnnounceOnSpawn
EmitSoundOnPickup
StaminaDamageOnEmbed
IgniteOnMeleeHit
UseDelayOnMeleeHit
DamageOnToolInteract
DamageOnInteract
SolutionInjectOnProjectileHit
and a lot more.

Think of it as device linking in yaml. This should open up a lot of new possibilities for yaml contributors as well.

Technical details

The PR is large in size, but the code is really simple.
I moved all trigger code from the Explosion namespace and other places into its own Trigger namespace, so that all existing trigger effects can be easily found. Since the triggers and trigger effects are very modular most of them are in their own system handling only a few subscriptions.
All code that has been moved to Shared should now be properly networked and predicted. A few exeptions are trigger effects that don't exist in shared, for example atmos and chat code.

Triggers and trigger effects now have a key string that is passed into the Trigger method and the TriggerEvent, and each effect will only be activated if the key is the same. This allows us to have multiple triggers on the same entity without them interfering with each other.

Conditions like Whitelists and UseDelays have been removed from all trigger effects, and are now instead checked in an AttemptTriggerEvent. This allows us to move them into their own components so they can be used with any trigger.

Each trigger effect now has the TargetUser datafield, allowing us to select if the effect will target the triggered entity or the user of the trigger. Before this was working different on a case by case basis and some trigger components were renamed to make this more consistent.

TODO before this can be merged:

  • Do a lot of in-game testing to make sure all items are still working correctly.

Media

trigger.mp4

Requirements

Breaking changes

All trigger related components and systems have been moved to the Content.Shared.Client/Shared/Server.Trigger.* namespace.

SharedTriggerOnProximityComponent was removed. Use TriggerOnProximity instead.
TriggerOnTimedCollideComponent now uses TimeSpans instead of accumulating frametime.
TriggerOnMobStateChangeComponent no longer automatically starts timer triggers, this has to be explicitly linked via the same trigger key now.
TriggerWhenEmptyComponent has been renamed to TriggerOnEmptyGunshotComponent.
TriggerImplantActionComponent has been renamed to TriggerOnActivateImplantComponent.
DamageUserOnTriggerComponent has been removed in favor of DamageOnTriggerComponent. Set TargetUser to true to obtain the same result.
GhostKickUserOnTriggerComponent has been removed in favor of GhostKickOnTriggerComponent. Set TargetUser to true to obtain the same result.
SoundOnTriggerComponent has been removed because it was a duplicate of EmitSoundOnTriggerComponent.
The CanAnchor and CanUnanchor datafields in AnchorOnTriggerComponent now actually work (they did not do anything before).
GibOnTriggerComponent now targets the owning entity by default. Set TargetUser to true to keep the previous effect.
ShockOnTriggerComponent now has a TargetContainer bool for when it's used for equipment. Otherwise it will shock the owning entity.
ShockOnTriggerComponent no longer has the Cooldown datafield. Use UseDelayOnTriggerComponent and UseDelayTriggerConditionComponent instead.
IgniteOnTriggerComponent no longer plays a sound, use EmitSoundOnTrigger instead.
PolymorphOnTriggerComponent now targets the owning entity by default. Set TargetUser to true to keep the previous effect.
RattleComponent has been renamed to RattleOnTrigger and targets the owning entity by default. Set TargetUser to true to keep the previous effect.
MouseTrapComponent now uses ItemToggleComponent for arming and dearming.
MouseTrapSystem has been moved to Content.Shared.Mousetrap.
ActiveListenerComponent has been moved to Content.Shared.Speech.
OnUseTimerTrigger has been split up into multiple components. Use TimerTrigger instead.
SignallerComponent no longer triggers when receiving a signal. Add TriggerOnSignalComponent to keep this effect.
TimerTrigger does not automatically activate when the item is used in hand, stuck on a wall or when receiving a device link signal. Use TriggerOnUse, TriggerOnStuck, or TriggerOnSignal to send a trigger to start the timer.
ActiveTimerTrigger is now a pure marker component without duplicating the datafields in OnUseTimerTrigger.
UseVerbInstead has been removed from TimerTrigger. Use TriggerOnVerb instead to start the timer.
AutomatedTimerComponent has been removed, use other triggers to start the timer the way you need.

TriggerWhitelistComponent has been renamed to WhitelistTriggerConditionComponent.
All whitelist checks have been removed from trigger components. Add WhitelistTriggerConditionComponent to obtain the same result.
All usedelay checks have been removed from trigger components. Use UseDelayOnTriggerComponent and UseDelayTriggerConditionComponent instead.

Changelog
no player facing changes

@PJBot PJBot added S: Needs Review Status: Requires additional reviews before being fully accepted. Not to be replaced by S: Approved. S: Untriaged Status: Indicates an item has not been triaged and doesn't have appropriate labels. Changes: Map Changes: Might require knowledge of mapping. labels Jul 17, 2025
@github-actions github-actions bot added the size/M Denotes a PR that changes 100-999 lines. label Jul 17, 2025
@perryprog
Copy link
Contributor

are you okay

@slarticodefast
Copy link
Member Author

slarticodefast commented Jul 17, 2025

are you okay

I'm good now that it's (almost) finished 😄
I fear for whoever has to review it.

@ScarKy0
Copy link
Contributor

ScarKy0 commented Jul 17, 2025

You bought me at "endless admeme potential"

Copy link
Contributor

@perryprog perryprog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope you don't mind the nitpicks. I personally appreciate getting them, so.

Haven't started testing much yet and I'm scared to look at much less review the yaml changes.

/// If TargetUser is true and they have that component they will be toggled instead.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ItemToggleOnTriggerComponent : BaseXOnTriggerComponent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably get a field that allows hiding the popups that ItemToggle generates. (See: walking over a mousetrap generates a "The mousetrap was deactivated." message.)

@perryprog
Copy link
Contributor

perryprog commented Jul 18, 2025

Oh, yeah, I forgot to mention I wasn’t checking what was actually new code and what was just moved. Feel free to ignore anything on old code that you weren’t doing a clean-up pass on.

mobState:
- Dead
- type: EmitSoundOnTrigger
predicted: true
Copy link
Contributor

@perryprog perryprog Jul 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait you use PlayPredicted not PlayLocal so uhhhh I don't know why this fixes this (yet)

Ah, it's really just for the person who kills the implanted entity, since they get marked as the trigger's user. So what I said above.

@perryprog
Copy link
Contributor

perryprog commented Jul 19, 2025

I love it when github double posts my review, I delete the duplicates, and then that also deletes some but not all of the comments from the review I was trying to keep. Very cool.

If someone in here has email notifications for this thread, checking what they were would be appreciated—I think one of the comments was about an issue with an error sprite getting predicted for ExplodesOnTrigger (reproduction being detonate a C4 and watch its sprite at the end). I already forgot what the others were (or if there were others deleted).

Edit: oh, one of them was RepulseAttractOnTrigger not getting predicted on client, but that's probably an issue with SharedThrowingSystem.

@PJBot PJBot added the size/XL Denotes a PR that changes 5000+ lines. label Aug 2, 2025
@PJBot PJBot removed the size/XL Denotes a PR that changes 5000+ lines. label Aug 2, 2025
@beck-thompson beck-thompson added P1: High Priority: Higher priority than other items, but isn't an emergency. T: New Feature Type: New feature or content, or extending existing content T: Refactor Type: Refactor of notable amount of codebase T: Cleanup Type: Code clean-up, without being a full refactor or feature A: Core Tech Area: Underlying core tech for the game and the Github repository. D1: High Difficulty: Extensive codebase knowledge required. and removed S: Awaiting Changes Status: Changes are required before another review can happen S: Untriaged Status: Indicates an item has not been triaged and doesn't have appropriate labels. labels Aug 2, 2025
Copy link
Member

@beck-thompson beck-thompson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Putting do not merge because I think scar wants to take a look and we should wait until stable is finished when we merge this. As soon as that's ready though lets get a full 2 weeks of testing 🥳 (I think it should be pretty bug free!)

@PJBot PJBot added the S: Approved Status: Reviewed and approved by at least one maintainer; a PR may require another approval. label Aug 2, 2025
@beck-thompson beck-thompson added the S: DO NOT MERGE Status: Open item that should NOT be merged. DNM. Allows test to run unlike draft. label Aug 2, 2025
Copy link
Contributor

github-actions bot commented Aug 3, 2025

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot added the S: Merge Conflict Status: Needs to resolve merge conflicts before it can be accepted label Aug 3, 2025
@PJBot PJBot added the size/XL Denotes a PR that changes 5000+ lines. label Aug 3, 2025
@github-actions github-actions bot removed the S: Merge Conflict Status: Needs to resolve merge conflicts before it can be accepted label Aug 3, 2025
Copy link
Contributor

@ScarKy0 ScarKy0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most admin abusable PR ever and its not even my birthday.

@ScarKy0
Copy link
Contributor

ScarKy0 commented Aug 3, 2025

image

@ScarKy0 ScarKy0 removed the S: DO NOT MERGE Status: Open item that should NOT be merged. DNM. Allows test to run unlike draft. label Aug 3, 2025
@ScarKy0 ScarKy0 merged commit 2c40a95 into space-wizards:master Aug 3, 2025
10 checks passed
@slarticodefast slarticodefast deleted the predicttriggers2 branch August 3, 2025 19:21
@perryprog
Copy link
Contributor

oof ouchie my merge conflicts

This was referenced Aug 3, 2025
southbridge-fur pushed a commit to southbridge-fur/space-station-14 that referenced this pull request Aug 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A: Core Tech Area: Underlying core tech for the game and the Github repository. Changes: Map Changes: Might require knowledge of mapping. D1: High Difficulty: Extensive codebase knowledge required. P1: High Priority: Higher priority than other items, but isn't an emergency. S: Approved Status: Reviewed and approved by at least one maintainer; a PR may require another approval. size/XL Denotes a PR that changes 5000+ lines. T: Cleanup Type: Code clean-up, without being a full refactor or feature T: New Feature Type: New feature or content, or extending existing content T: Refactor Type: Refactor of notable amount of codebase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants