mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 18:31:17 +03:00
* Solving #43723 by making zombies clumsy (they are unable to hold anything in their hands, passing items to them will fail). Also made zombies uncuff themselves whenever they transform (seeing as they no longer lose their hands meaning that cuffs would otherwise remain on them) * Cleaning up the code * Renamed ClumsyHoldStatusEffectComponent.cs to ClumsyGrabStatusEffectComponent.cs (and renamed associated files). I think "ClumsyGrab" works better than "ClumsyHold" (as the effect happens when the item is first grabbed, not whilst the item is being held). everything still works Reordered appropriate functions in ClumsyStatusTest.cs and ClumsyStatusEffectSystem.cs to retain alphabetical order. * oopsies :p * Appeased the YAMLLinter (at the cost of a somewhat hackier implementation). ZombieSystem.Transform.cs now applies the zombie clumsiness via creating a new PermanentStatusEffectComponent with StatusEffectClumsyZombie as its status effect. * requested changes have been made (also re-added newline to end of clumsy-component.ftl because it has a newline at the end of it on the master branch, I guess it's probably supposed to be there) * reordering so private members (including new one) are before the internal member * hands are forcibly emptied - even if something's glued to them. * UnZombify should remove the lingering ZombieClumsy status effect (still needs to revert the rest of the zombification process, but at least the new bit I added can be undone) * Minor refactor of my own refactor. New order of operations: 1. Uncuffs zombie (if needed) 2. Attempts to `DropAll` items from hands 3. Iterates through hand containers to empty them (removes otherwise unremovable items from hands) Will refactor process to force drop all items if #45844 becomes implementable + is merged. * formatting * how the hell did the metabolizer code go missing!? * uncuffing now happens in a safer way less likely to accidentally infinitely loop * tweaking that one comment I was asked to tweak (this is unrelated to the hand malarkey) hopefully it won't lead to any merge conflicts later on * formatting * file style --------- Co-authored-by: Pok245 <113675512+Pok27@users.noreply.github.com>
201 lines
8.5 KiB
C#
201 lines
8.5 KiB
C#
using Content.Shared.CCVar;
|
|
using Content.Shared.Chemistry.Events;
|
|
using Content.Shared.Climbing.Events;
|
|
using Content.Shared.Climbing.Systems;
|
|
using Content.Shared.Clumsy.Components;
|
|
using Content.Shared.Damage.Systems;
|
|
using Content.Shared.Hands;
|
|
using Content.Shared.IdentityManagement;
|
|
using Content.Shared.Medical;
|
|
using Content.Shared.Popups;
|
|
using Content.Shared.Random.Helpers;
|
|
using Content.Shared.StatusEffectNew;
|
|
using Content.Shared.Stunnable;
|
|
using Content.Shared.Throwing;
|
|
using Content.Shared.Weapons.Ranged.Events;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.Network;
|
|
using Robust.Shared.Timing;
|
|
|
|
namespace Content.Shared.Clumsy;
|
|
|
|
/// <summary>
|
|
/// Handles status effects which cause the afflicted to randomly fail certain events.
|
|
/// </summary>
|
|
public sealed partial class ClumsyStatusEffectSystem : EntitySystem
|
|
{
|
|
[Dependency] private SharedStunSystem _stun = default!;
|
|
[Dependency] private SharedAudioSystem _audio = default!;
|
|
[Dependency] private SharedPopupSystem _popup = default!;
|
|
|
|
[Dependency] private ClimbSystem _climb = default!;
|
|
[Dependency] private DamageableSystem _damageable = default!;
|
|
[Dependency] private IConfigurationManager _cfg = default!;
|
|
[Dependency] private IGameTiming _timing = default!;
|
|
[Dependency] private INetManager _net = default!;
|
|
|
|
#region Subscriptions
|
|
|
|
/// <summary> Clumsy people are bad at baseball! </summary>
|
|
[SubscribeLocalEvent]
|
|
private void OnCatchAttemptEvent(Entity<ClumsyCatchStatusEffectComponent> status, ref StatusEffectRelayedEvent<CatchAttemptEvent> args)
|
|
{
|
|
if (args.Args.Cancelled
|
|
|| !SharedRandomExtensions.PredictedProb(_timing, status.Comp.ClumsyChance, GetNetEntity(status), GetNetEntity(args.AppliedTo)))
|
|
return;
|
|
|
|
// Song and dance :o|
|
|
var ev = args.Args;
|
|
ev.Cancelled = true;
|
|
args.Args = ev;
|
|
|
|
if (status.Comp.FailDamage != null)
|
|
_damageable.ChangeDamage(args.AppliedTo, status.Comp.FailDamage, origin: args.Args.Item);
|
|
|
|
var identity = Identity.Entity(args.AppliedTo, EntityManager);
|
|
|
|
var selfMessage = status.Comp.SelfFailedMessage == null
|
|
? null
|
|
: Loc.GetString(status.Comp.SelfFailedMessage, ("item", args.Args.Item));
|
|
var othersMessage = status.Comp.OtherFailedMessage == null
|
|
? null
|
|
: Loc.GetString(status.Comp.OtherFailedMessage, ("item", args.Args.Item), ("catcher", identity));
|
|
|
|
_popup.PopupEntity(selfMessage, othersMessage, args.AppliedTo, args.AppliedTo);
|
|
|
|
// _audio.PlayPredicted doesn't play nice with collision events so we need PlayPvs
|
|
// exit early for clients so the sound doesn't play twice
|
|
if (_net.IsClient)
|
|
return;
|
|
|
|
_audio.PlayPvs(status.Comp.ClumsySound, args.AppliedTo);
|
|
}
|
|
|
|
/// <summary> Clumsy people shock themselves with defibrillators! </summary>
|
|
[SubscribeLocalEvent]
|
|
private void OnBeforeDefibrillatorZapsEvent(Entity<ClumsyDefibStatusEffectComponent> status, ref StatusEffectRelayedEvent<SelfBeforeDefibrillatorZapsEvent> args)
|
|
{
|
|
if (!SharedRandomExtensions.PredictedProb(_timing, status.Comp.ClumsyChance, GetNetEntity(status), GetNetEntity(args.AppliedTo)))
|
|
return;
|
|
|
|
var ev = args.Args;
|
|
ev.DefibTarget = ev.EntityUsingDefib;
|
|
|
|
if (status.Comp.FailedMessage != null)
|
|
_popup.PopupEntity(Loc.GetString(status.Comp.FailedMessage), args.AppliedTo, args.AppliedTo);
|
|
|
|
_audio.PlayPredicted(status.Comp.ClumsySound, args.AppliedTo, args.AppliedTo);
|
|
}
|
|
|
|
/// <summary> Especially clumsy people may sometimes fail to pick things up, and fail to hold on to things they are given.</summary>
|
|
[SubscribeLocalEvent]
|
|
private void OnBeforeEquippingHandEvent(Entity<ClumsyGrabStatusEffectComponent> status,
|
|
ref StatusEffectRelayedEvent<BeforeEquippingHandEvent> args)
|
|
{
|
|
if (args.Args.Cancelled
|
|
|| !SharedRandomExtensions.PredictedProb(_timing, status.Comp.ClumsyChance, GetNetEntity(status), GetNetEntity(args.AppliedTo)))
|
|
return;
|
|
|
|
var ev = args.Args;
|
|
ev.Cancelled = true;
|
|
args.Args = ev;
|
|
|
|
var selfMessage = status.Comp.SelfFailedMessage == null ? null : Loc.GetString(status.Comp.SelfFailedMessage, ("item", args.Args.Item));
|
|
var othersMessage = status.Comp.OtherFailedMessage == null ? null : Loc.GetString(status.Comp.OtherFailedMessage, ("item", args.Args.Item));
|
|
_popup.PopupEntity(selfMessage, othersMessage, args.AppliedTo, args.AppliedTo);
|
|
}
|
|
|
|
/// <summary> Clumsy people can't be trusted with guns! </summary>
|
|
[SubscribeLocalEvent]
|
|
private void OnBeforeGunShotEvent(Entity<ClumsyGunStatusEffectComponent> status, ref StatusEffectRelayedEvent<SelfBeforeGunShotEvent> args)
|
|
{
|
|
if (args.Args.Cancelled
|
|
|| args.Args.Gun.Comp.ClumsyProof
|
|
|| !SharedRandomExtensions.PredictedProb(_timing, status.Comp.ClumsyChance, GetNetEntity(status), GetNetEntity(args.AppliedTo)))
|
|
return;
|
|
|
|
args.Args.Cancel();
|
|
|
|
if (status.Comp.FailDamage != null)
|
|
_damageable.ChangeDamage(args.AppliedTo, status.Comp.FailDamage, origin: args.Args.Gun);
|
|
|
|
_stun.TryUpdateParalyzeDuration(args.AppliedTo, status.Comp.StunDuration);
|
|
|
|
if (status.Comp.FailedMessage != null)
|
|
_popup.PopupEntity(Loc.GetString(status.Comp.FailedMessage, ("gun", args.Args.Gun)), args.AppliedTo, args.AppliedTo);
|
|
|
|
// SelfBeforeGunShotEvent is raised on server so _audio.PlayPredicted fails to play locally
|
|
if (_net.IsClient)
|
|
return;
|
|
|
|
// Apply salt to the wound ("Honk!") (No idea what this comment means) :o)
|
|
_audio.PlayPvs(status.Comp.GunShootFailSound, args.Args.Gun);
|
|
_audio.PlayPvs(status.Comp.ClumsySound, args.AppliedTo);
|
|
}
|
|
|
|
/// <summary> Clumsy people sometimes inject themselves! </summary>
|
|
[SubscribeLocalEvent]
|
|
private void OnBeforeInjectEvent(Entity<ClumsyInjectorStatusEffectComponent> status, ref StatusEffectRelayedEvent<SelfBeforeInjectEvent> args)
|
|
{
|
|
if (!SharedRandomExtensions.PredictedProb(_timing, status.Comp.ClumsyChance, GetNetEntity(status), GetNetEntity(args.AppliedTo)))
|
|
return;
|
|
|
|
var ev = args.Args;
|
|
ev.TargetGettingInjected = ev.EntityUsingInjector;
|
|
|
|
if (status.Comp.FailedMessage != null)
|
|
ev.OverrideMessage = Loc.GetString(status.Comp.FailedMessage);
|
|
|
|
_audio.PlayPredicted(status.Comp.ClumsySound, args.AppliedTo, args.AppliedTo);
|
|
}
|
|
|
|
/// <summary> Clumsy people have a blood feud with tables! </summary>
|
|
[SubscribeLocalEvent]
|
|
private void OnBeforeClimbEvent(Entity<ClumsyVaultStatusEffectComponent> status, ref StatusEffectRelayedEvent<SelfBeforeClimbEvent> args)
|
|
{
|
|
if (args.Args.Cancelled
|
|
|| !_cfg.GetCVar(CCVars.GameTableBonk)
|
|
|| !SharedRandomExtensions.PredictedProb(_timing, status.Comp.ClumsyChance, GetNetEntity(status), GetNetEntity(args.AppliedTo)))
|
|
return;
|
|
|
|
args.Args.Cancel();
|
|
|
|
_climb.Bonk(args.Args.BeingClimbedOn.Owner, args.Args.GettingPutOnTable);
|
|
|
|
var putOnTable = Identity.Entity(args.Args.GettingPutOnTable, EntityManager);
|
|
var puttingOnTable = Identity.Entity(args.Args.PuttingOnTable, EntityManager);
|
|
|
|
if (args.Args.PuttingOnTable == args.Args.GettingPutOnTable)
|
|
{
|
|
// You are slamming yourself onto the table.
|
|
|
|
var selfMessage = status.Comp.SelfFailedMessage == null
|
|
? null
|
|
: Loc.GetString(status.Comp.SelfFailedMessage, ("bonkable", args.Args.BeingClimbedOn));
|
|
var othersMessage = status.Comp.OtherFailedMessage == null
|
|
? null
|
|
: Loc.GetString(status.Comp.OtherFailedMessage, ("victim", putOnTable), ("bonkable", args.Args.BeingClimbedOn));
|
|
|
|
_popup.PopupEntity(selfMessage, othersMessage, args.AppliedTo, args.AppliedTo);
|
|
}
|
|
else
|
|
{
|
|
// Someone else slammed you onto the table.
|
|
// This is only run in server so you need to use popup entity.
|
|
|
|
var message = status.Comp.ForcedMessage == null
|
|
? null
|
|
: Loc.GetString(status.Comp.ForcedMessage,
|
|
("bonker", puttingOnTable),
|
|
("victim", putOnTable),
|
|
("bonkable", args.Args.BeingClimbedOn));
|
|
|
|
_popup.PopupEntity(message, args.AppliedTo);
|
|
}
|
|
|
|
_audio.PlayPredicted(status.Comp.ClumsySound, args.Args.GettingPutOnTable, args.Args.GettingPutOnTable);
|
|
}
|
|
|
|
#endregion
|
|
}
|