Prevent pacified players from throwing dangerous stuff (#22268)

This commit is contained in:
KP
2023-12-11 15:40:22 -08:00
committed by GitHub
parent 5143030baf
commit d0085f9428
9 changed files with 136 additions and 20 deletions

View File

@@ -1,4 +1,5 @@
using Content.Server.Damage.Components; using Content.Server.Damage.Components;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Throwing; using Content.Shared.Throwing;
@@ -12,6 +13,19 @@ namespace Content.Server.Damage.Systems
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<DamageOnLandComponent, LandEvent>(DamageOnLand); SubscribeLocalEvent<DamageOnLandComponent, LandEvent>(DamageOnLand);
SubscribeLocalEvent<DamageOnLandComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
}
/// <summary>
/// Prevent Pacified entities from throwing damaging items.
/// </summary>
private void OnAttemptPacifiedThrow(Entity<DamageOnLandComponent> ent, ref AttemptPacifiedThrowEvent args)
{
// Allow healing projectiles, forbid any that do damage:
if (ent.Comp.Damage.Any())
{
args.Cancel("pacified-cannot-throw");
}
} }
private void DamageOnLand(EntityUid uid, DamageOnLandComponent component, ref LandEvent args) private void DamageOnLand(EntityUid uid, DamageOnLandComponent component, ref LandEvent args)

View File

@@ -2,6 +2,7 @@ using System.Linq;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Body.Part; using Content.Shared.Body.Part;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Damage.Components; using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems; using Content.Shared.Damage.Systems;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
@@ -26,6 +27,12 @@ public sealed partial class EnsnareableSystem
SubscribeLocalEvent<EnsnaringComponent, StepTriggerAttemptEvent>(AttemptStepTrigger); SubscribeLocalEvent<EnsnaringComponent, StepTriggerAttemptEvent>(AttemptStepTrigger);
SubscribeLocalEvent<EnsnaringComponent, StepTriggeredEvent>(OnStepTrigger); SubscribeLocalEvent<EnsnaringComponent, StepTriggeredEvent>(OnStepTrigger);
SubscribeLocalEvent<EnsnaringComponent, ThrowDoHitEvent>(OnThrowHit); SubscribeLocalEvent<EnsnaringComponent, ThrowDoHitEvent>(OnThrowHit);
SubscribeLocalEvent<EnsnaringComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
}
private void OnAttemptPacifiedThrow(Entity<EnsnaringComponent> ent, ref AttemptPacifiedThrowEvent args)
{
args.Cancel("pacified-cannot-throw-snare");
} }
private void OnComponentRemove(EntityUid uid, EnsnaringComponent component, ComponentRemove args) private void OnComponentRemove(EntityUid uid, EnsnaringComponent component, ComponentRemove args)

View File

@@ -6,6 +6,7 @@ using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Clothing.Components; using Content.Shared.Clothing.Components;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Examine; using Content.Shared.Examine;
@@ -36,6 +37,7 @@ public sealed partial class PuddleSystem
SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped); SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<SpillableComponent, SolutionOverflowEvent>(OnOverflow); SubscribeLocalEvent<SpillableComponent, SolutionOverflowEvent>(OnOverflow);
SubscribeLocalEvent<SpillableComponent, SpillDoAfterEvent>(OnDoAfter); SubscribeLocalEvent<SpillableComponent, SpillDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<SpillableComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
} }
private void OnExamined(EntityUid uid, SpillableComponent component, ExaminedEvent args) private void OnExamined(EntityUid uid, SpillableComponent component, ExaminedEvent args)
@@ -152,6 +154,22 @@ public sealed partial class PuddleSystem
TrySplashSpillAt(uid, Transform(uid).Coordinates, drainedSolution, out _); TrySplashSpillAt(uid, Transform(uid).Coordinates, drainedSolution, out _);
} }
/// <summary>
/// Prevent Pacified entities from throwing items that can spill liquids.
/// </summary>
private void OnAttemptPacifiedThrow(Entity<SpillableComponent> ent, ref AttemptPacifiedThrowEvent args)
{
// Dont care about closed containers.
if (_openable.IsClosed(ent))
return;
// Dont care about empty containers.
if (!_solutionContainerSystem.TryGetSolution(ent, ent.Comp.SolutionName, out var solution))
return;
args.Cancel("pacified-cannot-throw-spill");
}
private void AddSpillVerb(EntityUid uid, SpillableComponent component, GetVerbsEvent<Verb> args) private void AddSpillVerb(EntityUid uid, SpillableComponent component, GetVerbsEvent<Verb> args)
{ {
if (!args.CanAccess || !args.CanInteract) if (!args.CanAccess || !args.CanInteract)

View File

@@ -204,9 +204,9 @@ namespace Content.Server.Hands.Systems
// Let other systems change the thrown entity (useful for virtual items) // Let other systems change the thrown entity (useful for virtual items)
// or the throw strength. // or the throw strength.
var ev = new BeforeThrowEvent(throwEnt, direction, throwStrength, player); var ev = new BeforeThrowEvent(throwEnt, direction, throwStrength, player);
RaiseLocalEvent(player, ev, false); RaiseLocalEvent(player, ref ev);
if (ev.Handled) if (ev.Cancelled)
return true; return true;
// This can grief the above event so we raise it afterwards // This can grief the above event so we raise it afterwards

View File

@@ -1,20 +1,57 @@
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
using Content.Shared.Throwing;
namespace Content.Shared.CombatMode.Pacification; namespace Content.Shared.CombatMode.Pacification;
/// <summary>
/// Raised when a Pacified entity attempts to throw something.
/// The throw is only permitted if this event is not cancelled.
/// </summary>
[ByRefEvent]
public struct AttemptPacifiedThrowEvent
{
public EntityUid ItemUid;
public EntityUid PlayerUid;
public AttemptPacifiedThrowEvent(EntityUid itemUid, EntityUid playerUid)
{
ItemUid = itemUid;
PlayerUid = playerUid;
}
public bool Cancelled { get; private set; } = false;
public string? CancelReasonMessageId { get; private set; }
/// <param name="reasonMessageId">
/// Localization string ID for the reason this event has been cancelled.
/// If null, a generic message will be shown to the player.
/// Note that any supplied localization string MUST accept a '$projectile'
/// parameter specifying the name of the thrown entity.
/// </param>
public void Cancel(string? reasonMessageId = null)
{
Cancelled = true;
CancelReasonMessageId = reasonMessageId;
}
}
public sealed class PacificationSystem : EntitySystem public sealed class PacificationSystem : EntitySystem
{ {
[Dependency] private readonly AlertsSystem _alertsSystem = default!; [Dependency] private readonly AlertsSystem _alertsSystem = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SharedCombatModeSystem _combatSystem = default!; [Dependency] private readonly SharedCombatModeSystem _combatSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<PacifiedComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<PacifiedComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<PacifiedComponent, ComponentShutdown>(OnShutdown); SubscribeLocalEvent<PacifiedComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<PacifiedComponent, BeforeThrowEvent>(OnBeforeThrow);
SubscribeLocalEvent<PacifiedComponent, AttackAttemptEvent>(OnAttackAttempt); SubscribeLocalEvent<PacifiedComponent, AttackAttemptEvent>(OnAttackAttempt);
} }
@@ -47,4 +84,23 @@ public sealed class PacificationSystem : EntitySystem
_actionsSystem.SetEnabled(combatMode.CombatToggleActionEntity, true); _actionsSystem.SetEnabled(combatMode.CombatToggleActionEntity, true);
_alertsSystem.ClearAlert(uid, AlertType.Pacified); _alertsSystem.ClearAlert(uid, AlertType.Pacified);
} }
private void OnBeforeThrow(Entity<PacifiedComponent> ent, ref BeforeThrowEvent args)
{
var thrownItem = args.ItemUid;
var itemName = Identity.Entity(thrownItem, EntityManager);
// Raise an AttemptPacifiedThrow event and rely on other systems to check
// whether the candidate item is OK to throw:
var ev = new AttemptPacifiedThrowEvent(thrownItem, ent);
RaiseLocalEvent(thrownItem, ref ev);
if (!ev.Cancelled)
return;
args.Cancelled = true;
// Tell the player why they cant throw stuff:
var cannotThrowMessage = ev.CancelReasonMessageId ?? "pacified-cannot-throw";
_popup.PopupEntity(Loc.GetString(cannotThrowMessage, ("projectile", itemName)), ent, ent);
}
} }

View File

@@ -3,7 +3,7 @@ using Robust.Shared.GameStates;
namespace Content.Shared.CombatMode.Pacification; namespace Content.Shared.CombatMode.Pacification;
/// <summary> /// <summary>
/// Status effect that disables combat mode. /// Status effect that disables combat mode and restricts aggressive actions.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
[Access(typeof(PacificationSystem))] [Access(typeof(PacificationSystem))]

View File

@@ -1,11 +1,10 @@
using System.Numerics; using System.Numerics;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Throwing; using Content.Shared.Throwing;
using Content.Shared.Weapons.Ranged.Components;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
@@ -37,6 +36,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem
SubscribeLocalEvent<EmbeddableProjectileComponent, ThrowDoHitEvent>(OnEmbedThrowDoHit); SubscribeLocalEvent<EmbeddableProjectileComponent, ThrowDoHitEvent>(OnEmbedThrowDoHit);
SubscribeLocalEvent<EmbeddableProjectileComponent, ActivateInWorldEvent>(OnEmbedActivate); SubscribeLocalEvent<EmbeddableProjectileComponent, ActivateInWorldEvent>(OnEmbedActivate);
SubscribeLocalEvent<EmbeddableProjectileComponent, RemoveEmbeddedProjectileEvent>(OnEmbedRemove); SubscribeLocalEvent<EmbeddableProjectileComponent, RemoveEmbeddedProjectileEvent>(OnEmbedRemove);
SubscribeLocalEvent<EmbeddableProjectileComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
} }
private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent component, ActivateInWorldEvent args) private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent component, ActivateInWorldEvent args)
@@ -152,6 +152,14 @@ public abstract partial class SharedProjectileSystem : EntitySystem
{ {
public override DoAfterEvent Clone() => this; public override DoAfterEvent Clone() => this;
} }
/// <summary>
/// Prevent players with the Pacified status effect from throwing embeddable projectiles.
/// </summary>
private void OnAttemptPacifiedThrow(Entity<EmbeddableProjectileComponent> ent, ref AttemptPacifiedThrowEvent args)
{
args.Cancel("pacified-cannot-throw-embed");
}
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]

View File

@@ -1,20 +1,22 @@
using System.Numerics; using System.Numerics;
namespace Content.Shared.Throwing namespace Content.Shared.Throwing;
{
public sealed class BeforeThrowEvent : HandledEntityEventArgs
{
public BeforeThrowEvent(EntityUid itemUid, Vector2 direction, float throwStrength, EntityUid playerUid)
{
ItemUid = itemUid;
Direction = direction;
ThrowStrength = throwStrength;
PlayerUid = playerUid;
}
public EntityUid ItemUid { get; set; } [ByRefEvent]
public Vector2 Direction { get; } public struct BeforeThrowEvent
public float ThrowStrength { get; set;} {
public EntityUid PlayerUid { get; } public BeforeThrowEvent(EntityUid itemUid, Vector2 direction, float throwStrength, EntityUid playerUid)
{
ItemUid = itemUid;
Direction = direction;
ThrowStrength = throwStrength;
PlayerUid = playerUid;
} }
public EntityUid ItemUid { get; set; }
public Vector2 Direction { get; }
public float ThrowStrength { get; set;}
public EntityUid PlayerUid { get; }
public bool Cancelled = false;
} }

View File

@@ -0,0 +1,11 @@
## Messages shown to Pacified players when they try to do violence:
# With projectiles:
pacified-cannot-throw = You can't bring yourself to throw { THE($projectile) }, that could hurt someone!
# With embedding projectiles:
pacified-cannot-throw-embed = No way you can throw { THE($projectile) }, that could get lodged inside someone!
# With liquid-spilling projectiles:
pacified-cannot-throw-spill = You can't possibly throw { THE($projectile) }, that could spill nasty stuff on someone!
# With bolas and snares:
pacified-cannot-throw-snare = You can't throw { THE($projectile) }, what if someone trips?!