Bugfix: Guardian popup spam, mech attack blocker (#45013)

* guardian popup fixes

* actually use the entityqueries

* finish a thought challenge - difficulty impossible

* curse of fildrance

* can't believe he's done this
This commit is contained in:
Whatstone 2026-07-29 16:48:46 -04:00 • committed by GitHub
parent f196854844
commit 45f91a84a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 62 additions and 40 deletions

View file

@ -1,12 +1,14 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Guardian.Components;
/// <summary>
/// Given to guardians to monitor their link with the host.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState, AutoGenerateComponentPause]
public sealed partial class GuardianComponent : Component
{
/// <summary>
@ -50,4 +52,16 @@ public sealed partial class GuardianComponent : Component
/// </summary>
[DataField]
public SoundSpecifier DeathSound = new SoundPathSpecifier("/Audio/Voice/Human/malescream_guardian.ogg", AudioParams.Default.WithVariation(0.2f));
/// <summary>
/// The last time that the entity received an attack popup.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan LastAttackPopupTime;
/// <summary>
/// The delay between showing popups to the guardian when trying to attack its host.
/// </summary>
[DataField]
public TimeSpan AttackPopupDelay = TimeSpan.FromSeconds(1);
}

View file

@ -36,35 +36,13 @@ public sealed partial class GuardianSystem : EntitySystem
[Dependency] private SharedPopupSystem _popup = default!;
[Dependency] private SharedTransformSystem _transform = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GuardianCreatorComponent, UseInHandEvent>(OnCreatorUse);
SubscribeLocalEvent<GuardianCreatorComponent, AfterInteractEvent>(OnCreatorInteract);
SubscribeLocalEvent<GuardianCreatorComponent, ExaminedEvent>(OnCreatorExamine);
SubscribeLocalEvent<GuardianCreatorComponent, GuardianCreatorDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<GuardianComponent, ComponentShutdown>(OnGuardianShutdown);
SubscribeLocalEvent<GuardianComponent, MoveEvent>(OnGuardianMove);
SubscribeLocalEvent<GuardianComponent, DamageDealtEvent>(OnGuardianDamaged);
SubscribeLocalEvent<GuardianComponent, PlayerAttachedEvent>(OnGuardianPlayerAttached);
SubscribeLocalEvent<GuardianComponent, PlayerDetachedEvent>(OnGuardianPlayerDetached);
SubscribeLocalEvent<GuardianHostComponent, ComponentInit>(OnHostInit);
SubscribeLocalEvent<GuardianHostComponent, MoveEvent>(OnHostMove);
SubscribeLocalEvent<GuardianHostComponent, MobStateChangedEvent>(OnHostStateChange);
SubscribeLocalEvent<GuardianHostComponent, ComponentShutdown>(OnHostShutdown);
SubscribeLocalEvent<GuardianHostComponent, GuardianToggleActionEvent>(OnPerformAction);
SubscribeLocalEvent<GuardianComponent, AttackAttemptEvent>(OnGuardianAttackAttempt);
SubscribeLocalEvent<GuardianHostComponent, MechPilotRelayedEvent<GettingAttackedAttemptEvent>>(OnPilotAttackAttempt);
}
[Dependency] private EntityQuery<GuardianComponent> _guardianQuery;
[Dependency] private EntityQuery<GuardianHostComponent> _guardianHostQuery;
[SubscribeLocalEvent]
private void OnGuardianShutdown(Entity<GuardianComponent> ent, ref ComponentShutdown args)
{
if (!TryComp<GuardianHostComponent>(ent, out var hostComponent))
if (!_guardianHostQuery.TryComp(ent, out GuardianHostComponent? hostComponent))
return;
_container.Remove(ent.Owner, hostComponent.GuardianContainer);
@ -77,6 +55,7 @@ public sealed partial class GuardianSystem : EntitySystem
Dirty(ent, hostComponent);
}
[SubscribeLocalEvent]
private void OnPerformAction(Entity<GuardianHostComponent> ent, ref GuardianToggleActionEvent args)
{
if (args.Handled)
@ -94,9 +73,10 @@ public sealed partial class GuardianSystem : EntitySystem
args.Handled = true;
}
[SubscribeLocalEvent]
private void OnGuardianPlayerDetached(Entity<GuardianComponent> ent, ref PlayerDetachedEvent args)
{
if (!TryComp<GuardianHostComponent>(ent.Comp.Host, out var hostComponent) ||
if (!_guardianHostQuery.TryComp(ent.Comp.Host, out GuardianHostComponent? hostComponent) ||
TerminatingOrDeleted(ent.Owner))
{
PredictedQueueDel(ent.Owner);
@ -108,10 +88,11 @@ public sealed partial class GuardianSystem : EntitySystem
RetractGuardian((ent.Comp.Host.Value, hostComponent), (ent.Owner, ent.Comp));
}
[SubscribeLocalEvent]
private void OnGuardianPlayerAttached(Entity<GuardianComponent> ent, ref PlayerAttachedEvent args)
{
var host = ent.Comp.Host;
if (!HasComp<GuardianHostComponent>(host))
if (!_guardianHostQuery.HasComp(host))
{
PredictedQueueDel(ent.Owner);
ent.Comp.Host = null;
@ -122,12 +103,14 @@ public sealed partial class GuardianSystem : EntitySystem
_popup.PopupEntity(Loc.GetString("guardian-available"), host.Value, host.Value);
}
[SubscribeLocalEvent]
private void OnHostInit(Entity<GuardianHostComponent> ent, ref ComponentInit args)
{
ent.Comp.GuardianContainer = _container.EnsureContainer<ContainerSlot>(ent.Owner, "GuardianContainer");
_action.AddAction(ent.Owner, ref ent.Comp.ActionEntity, ent.Comp.Action);
}
[SubscribeLocalEvent]
private void OnHostShutdown(Entity<GuardianHostComponent> ent, ref ComponentShutdown args)
{
if (ent.Comp.HostedGuardian is not { } guardian)
@ -144,30 +127,46 @@ public sealed partial class GuardianSystem : EntitySystem
Dirty(ent);
}
[SubscribeLocalEvent]
private void OnGuardianAttackAttempt(Entity<GuardianComponent> ent, ref AttackAttemptEvent args)
{
if (args.Cancelled || args.Target != ent.Comp.Host)
return;
_popup.PopupCursor(Loc.GetString("guardian-attack-host"), ent.Owner, PopupType.LargeCaution);
if (_timing.CurTime >= ent.Comp.LastAttackPopupTime + ent.Comp.AttackPopupDelay)
{
_popup.PopupCursor(Loc.GetString("guardian-attack-host"), ent.Owner, PopupType.LargeCaution);
}
ent.Comp.LastAttackPopupTime = _timing.CurTime;
args.Cancel();
}
[SubscribeLocalEvent]
private void OnPilotAttackAttempt(Entity<GuardianHostComponent> ent,
ref MechPilotRelayedEvent<GettingAttackedAttemptEvent> args)
{
if (args.Args.Cancelled)
if (args.Args.Cancelled || args.Args.Attacker != ent.Comp.HostedGuardian)
return;
_popup.PopupCursor(Loc.GetString("guardian-attack-host"),
args.Args.Attacker,
PopupType.LargeCaution);
if (_guardianQuery.TryComp(args.Args.Attacker, out var guardian))
{
if (_timing.CurTime >= guardian.LastAttackPopupTime + guardian.AttackPopupDelay)
{
_popup.PopupCursor(Loc.GetString("guardian-attack-host"),
args.Args.Attacker,
PopupType.LargeCaution);
}
guardian.LastAttackPopupTime = _timing.CurTime;
}
args.Args.Cancelled = true;
}
[SubscribeLocalEvent]
private void ToggleGuardian(Entity<GuardianHostComponent> ent)
{
if (!TryComp<GuardianComponent>(ent.Comp.HostedGuardian, out var guardianComponent))
if (!_guardianQuery.TryComp(ent.Comp.HostedGuardian, out var guardianComponent))
return;
if (guardianComponent.GuardianLoose)
@ -179,6 +178,7 @@ public sealed partial class GuardianSystem : EntitySystem
/// <summary>
/// Adds the guardian host component to the user and spawns the guardian inside said component.
/// </summary>
[SubscribeLocalEvent]
private void OnCreatorUse(Entity<GuardianCreatorComponent> ent, ref UseInHandEvent args)
{
if (args.Handled)
@ -188,6 +188,7 @@ public sealed partial class GuardianSystem : EntitySystem
UseCreator(args.User, args.User, ent);
}
[SubscribeLocalEvent]
private void OnCreatorInteract(Entity<GuardianCreatorComponent> ent, ref AfterInteractEvent args)
{
if (args.Handled || args.Target == null || !args.CanReach)
@ -216,7 +217,7 @@ public sealed partial class GuardianSystem : EntitySystem
}
// If user is already a host don't duplicate.
if (HasComp<GuardianHostComponent>(target))
if (_guardianHostQuery.HasComp(target))
{
_popup.PopupEntity(Loc.GetString("guardian-already-present-invalid-creation"), user, user);
return;
@ -236,12 +237,13 @@ public sealed partial class GuardianSystem : EntitySystem
});
}
[SubscribeLocalEvent]
private void OnDoAfter(Entity<GuardianCreatorComponent> ent, ref GuardianCreatorDoAfterEvent args)
{
if (args.Handled || args.Args.Target == null || args.Cancelled || ent.Comp.Deleted || ent.Comp.Used)
return;
if (!_hands.IsHolding(args.Args.User, ent.Owner) || HasComp<GuardianHostComponent>(args.Args.Target))
if (!_hands.IsHolding(args.Args.User, ent.Owner) || _guardianHostQuery.HasComp(args.Args.Target))
return;
var hostXform = Transform(args.Args.Target.Value);
@ -253,9 +255,10 @@ public sealed partial class GuardianSystem : EntitySystem
_container.Insert(guardian, host.GuardianContainer);
host.HostedGuardian = guardian;
if (TryComp<GuardianComponent>(guardian, out var guardianComp))
if (_guardianQuery.TryComp(guardian, out GuardianComponent? guardianComp))
{
guardianComp.Host = args.Args.Target.Value;
Dirty(guardian, guardianComp);
_audio.PlayPredicted(ent.Comp.UsedSound,
ent.Owner,
args.Args.Target);
@ -278,10 +281,11 @@ public sealed partial class GuardianSystem : EntitySystem
/// <summary>
/// Triggers when the host receives damage which puts the host in either critical or killed state.
/// </summary>
[SubscribeLocalEvent]
private void OnHostStateChange(Entity<GuardianHostComponent> ent, ref MobStateChangedEvent args)
{
if (ent.Comp.HostedGuardian == null ||
!TryComp<GuardianComponent>(ent.Comp.HostedGuardian, out var guardianComp))
!_guardianQuery.TryComp(ent.Comp.HostedGuardian, out GuardianComponent? guardianComp))
return;
if (args.NewMobState == MobState.Critical)
@ -301,6 +305,7 @@ public sealed partial class GuardianSystem : EntitySystem
/// <summary>
/// Handles guardian receiving damage and splitting it with the host according to his defense percent.
/// </summary>
[SubscribeLocalEvent]
private void OnGuardianDamaged(Entity<GuardianComponent> ent, ref DamageDealtEvent args)
{
if (_timing.ApplyingState)
@ -321,6 +326,7 @@ public sealed partial class GuardianSystem : EntitySystem
/// <summary>
/// Triggers while trying to examine an activator to see if it's used.
/// </summary>
[SubscribeLocalEvent]
private void OnCreatorExamine(Entity<GuardianCreatorComponent> ent, ref ExaminedEvent args)
{
if (!ent.Comp.Used)
@ -332,9 +338,10 @@ public sealed partial class GuardianSystem : EntitySystem
/// <summary>
/// Called every time the host moves, to make sure the host and the guardian are not too far away from each other.
/// </summary>
[SubscribeLocalEvent]
private void OnHostMove(Entity<GuardianHostComponent> ent, ref MoveEvent args)
{
if (!TryComp<GuardianComponent>(ent.Comp.HostedGuardian, out var guardianComponent) ||
if (!_guardianQuery.TryComp(ent.Comp.HostedGuardian, out GuardianComponent? guardianComponent) ||
!guardianComponent.GuardianLoose)
{
return;
@ -346,6 +353,7 @@ public sealed partial class GuardianSystem : EntitySystem
/// <summary>
/// Called every time the guardian moves: makes sure it's not out of it's allowed distance.
/// </summary>
[SubscribeLocalEvent]
private void OnGuardianMove(Entity<GuardianComponent> ent, ref MoveEvent args)
{
if (!ent.Comp.GuardianLoose)