Prevent pacified players from throwing dangerous stuff (#22268)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Damage.Components;
|
||||
using Content.Shared.CombatMode.Pacification;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Throwing;
|
||||
|
||||
@@ -12,6 +13,19 @@ namespace Content.Server.Damage.Systems
|
||||
{
|
||||
base.Initialize();
|
||||
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)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Linq;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.CombatMode.Pacification;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
@@ -26,6 +27,12 @@ public sealed partial class EnsnareableSystem
|
||||
SubscribeLocalEvent<EnsnaringComponent, StepTriggerAttemptEvent>(AttemptStepTrigger);
|
||||
SubscribeLocalEvent<EnsnaringComponent, StepTriggeredEvent>(OnStepTrigger);
|
||||
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)
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.CombatMode.Pacification;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Examine;
|
||||
@@ -36,6 +37,7 @@ public sealed partial class PuddleSystem
|
||||
SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<SpillableComponent, SolutionOverflowEvent>(OnOverflow);
|
||||
SubscribeLocalEvent<SpillableComponent, SpillDoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<SpillableComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
|
||||
}
|
||||
|
||||
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 _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prevent Pacified entities from throwing items that can spill liquids.
|
||||
/// </summary>
|
||||
private void OnAttemptPacifiedThrow(Entity<SpillableComponent> ent, ref AttemptPacifiedThrowEvent args)
|
||||
{
|
||||
// Don’t care about closed containers.
|
||||
if (_openable.IsClosed(ent))
|
||||
return;
|
||||
|
||||
// Don’t 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)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
|
||||
@@ -204,9 +204,9 @@ namespace Content.Server.Hands.Systems
|
||||
// Let other systems change the thrown entity (useful for virtual items)
|
||||
// or the throw strength.
|
||||
var ev = new BeforeThrowEvent(throwEnt, direction, throwStrength, player);
|
||||
RaiseLocalEvent(player, ev, false);
|
||||
RaiseLocalEvent(player, ref ev);
|
||||
|
||||
if (ev.Handled)
|
||||
if (ev.Cancelled)
|
||||
return true;
|
||||
|
||||
// This can grief the above event so we raise it afterwards
|
||||
|
||||
@@ -1,20 +1,57 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Throwing;
|
||||
|
||||
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
|
||||
{
|
||||
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly SharedCombatModeSystem _combatSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PacifiedComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<PacifiedComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<PacifiedComponent, BeforeThrowEvent>(OnBeforeThrow);
|
||||
SubscribeLocalEvent<PacifiedComponent, AttackAttemptEvent>(OnAttackAttempt);
|
||||
}
|
||||
|
||||
@@ -47,4 +84,23 @@ public sealed class PacificationSystem : EntitySystem
|
||||
_actionsSystem.SetEnabled(combatMode.CombatToggleActionEntity, true);
|
||||
_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 can’t throw stuff:
|
||||
var cannotThrowMessage = ev.CancelReasonMessageId ?? "pacified-cannot-throw";
|
||||
_popup.PopupEntity(Loc.GetString(cannotThrowMessage, ("projectile", itemName)), ent, ent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.GameStates;
|
||||
namespace Content.Shared.CombatMode.Pacification;
|
||||
|
||||
/// <summary>
|
||||
/// Status effect that disables combat mode.
|
||||
/// Status effect that disables combat mode and restricts aggressive actions.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(PacificationSystem))]
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.CombatMode.Pacification;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
@@ -37,6 +36,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem
|
||||
SubscribeLocalEvent<EmbeddableProjectileComponent, ThrowDoHitEvent>(OnEmbedThrowDoHit);
|
||||
SubscribeLocalEvent<EmbeddableProjectileComponent, ActivateInWorldEvent>(OnEmbedActivate);
|
||||
SubscribeLocalEvent<EmbeddableProjectileComponent, RemoveEmbeddedProjectileEvent>(OnEmbedRemove);
|
||||
SubscribeLocalEvent<EmbeddableProjectileComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
|
||||
}
|
||||
|
||||
private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent component, ActivateInWorldEvent args)
|
||||
@@ -152,6 +152,14 @@ public abstract partial class SharedProjectileSystem : EntitySystem
|
||||
{
|
||||
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]
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
using System.Numerics;
|
||||
|
||||
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;
|
||||
}
|
||||
namespace Content.Shared.Throwing;
|
||||
|
||||
public EntityUid ItemUid { get; set; }
|
||||
public Vector2 Direction { get; }
|
||||
public float ThrowStrength { get; set;}
|
||||
public EntityUid PlayerUid { get; }
|
||||
[ByRefEvent]
|
||||
public struct BeforeThrowEvent
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
11
Resources/Locale/en-US/pacified/pacified.ftl
Normal file
11
Resources/Locale/en-US/pacified/pacified.ftl
Normal 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?!
|
||||
Reference in New Issue
Block a user