Convert suicide to ecs (#8091)

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
wrexbe
2022-05-12 05:05:16 -07:00
committed by GitHub
parent 6903209a31
commit 089e40a061
15 changed files with 362 additions and 282 deletions

View File

@@ -1,29 +0,0 @@
using Content.Server.Chat.Managers;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
namespace Content.Server.Act
{
[RequiresExplicitImplementation]
public interface ISuicideAct
{
public SuicideKind Suicide(EntityUid victim, IChatManager chat);
}
public enum SuicideKind
{
Special, //Doesn't damage the mob, used for "weird" suicides like gibbing
//Damage type suicides
Blunt,
Slash,
Piercing,
Heat,
Shock,
Cold,
Poison,
Radiation,
Asphyxiation,
Bloodloss
}
}

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Text;
using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers;

View File

@@ -36,122 +36,9 @@ namespace Content.Server.Chat.Commands
public string Help => Loc.GetString("suicide-command-help-text");
private void DealDamage(ISuicideAct suicide, IChatManager chat, EntityUid target)
{
var kind = suicide.Suicide(target, chat);
if (kind != SuicideKind.Special)
{
// TODO SUICIDE ..heh.. anyway, someone should fix this mess.
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
DamageSpecifier damage = new(kind switch
{
SuicideKind.Blunt => prototypeManager.Index<DamageTypePrototype>("Blunt"),
SuicideKind.Slash => prototypeManager.Index<DamageTypePrototype>("Slash"),
SuicideKind.Piercing => prototypeManager.Index<DamageTypePrototype>("Piercing"),
SuicideKind.Heat => prototypeManager.Index<DamageTypePrototype>("Heat"),
SuicideKind.Shock => prototypeManager.Index<DamageTypePrototype>("Shock"),
SuicideKind.Cold => prototypeManager.Index<DamageTypePrototype>("Cold"),
SuicideKind.Poison => prototypeManager.Index<DamageTypePrototype>("Poison"),
SuicideKind.Radiation => prototypeManager.Index<DamageTypePrototype>("Radiation"),
SuicideKind.Asphyxiation => prototypeManager.Index<DamageTypePrototype>("Asphyxiation"),
SuicideKind.Bloodloss => prototypeManager.Index<DamageTypePrototype>("Bloodloss"),
_ => prototypeManager.Index<DamageTypePrototype>("Blunt")
},
200);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(target, damage, true);
}
}
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = shell.Player as IPlayerSession;
if (player == null)
{
shell.WriteLine(Loc.GetString("shell-cannot-run-command-from-server"));
return;
}
if (player.Status != SessionStatus.InGame || player.AttachedEntity == null)
return;
var chat = IoCManager.Resolve<IChatManager>();
var mind = player.ContentData()?.Mind;
// This check also proves mind not-null for at the end when the mob is ghosted.
if (mind?.OwnedComponent?.Owner is not {Valid: true} owner)
{
shell.WriteLine("You don't have a mind!");
return;
}
//Checks to see if the player is dead.
if(_entities.TryGetComponent<MobStateComponent>(owner, out var mobState) && mobState.IsDead())
{
shell.WriteLine(Loc.GetString("suicide-command-already-dead"));
return;
}
//Checks to see if the CannotSuicide tag exits, ghosts instead.
if(EntitySystem.Get<TagSystem>().HasTag(owner, "CannotSuicide"))
{
if (!EntitySystem.Get<GameTicker>().OnGhostAttempt(mind, true))
{
shell?.WriteLine("You can't ghost right now.");
return;
}
return;
}
//TODO: needs to check if the mob is actually alive
//TODO: maybe set a suicided flag to prevent resurrection?
EntitySystem.Get<AdminLogSystem>().Add(LogType.Suicide,
$"{_entities.ToPrettyString(player.AttachedEntity.Value):player} is committing suicide");
// Held item suicide
if (_entities.TryGetComponent(owner, out HandsComponent handsComponent)
&& handsComponent.ActiveHandEntity is EntityUid item)
{
var suicide = _entities.GetComponents<ISuicideAct>(item).FirstOrDefault();
if (suicide != null)
{
DealDamage(suicide, chat, owner);
return;
}
}
// Get all entities in range of the suicider
var entities = EntitySystem.Get<EntityLookupSystem>().GetEntitiesInRange(owner, 1, LookupFlags.Approximate | LookupFlags.Anchored).ToArray();
if (entities.Length > 0)
{
foreach (var entity in entities)
{
if (_entities.HasComponent<SharedItemComponent>(entity))
continue;
var suicide = _entities.GetComponents<ISuicideAct>(entity).FirstOrDefault();
if (suicide != null)
{
DealDamage(suicide, chat, owner);
return;
}
}
}
// Default suicide, bite your tongue
var othersMessage = Loc.GetString("suicide-command-default-text-others",("name", owner));
owner.PopupMessageOtherClients(othersMessage);
var selfMessage = Loc.GetString("suicide-command-default-text-self");
owner.PopupMessage(selfMessage);
DamageSpecifier damage = new(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Bloodloss"), 200);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(owner, damage, true);
// Prevent the player from returning to the body.
// Note that mind cannot be null because otherwise owner would be null.
EntitySystem.Get<GameTicker>().OnGhostAttempt(mind!, false);
EntitySystem.Get<SuicideSystem>().Suicide(shell);
}
}
}

View File

@@ -0,0 +1,150 @@
using Content.Server.Act;
using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking;
using Content.Server.Hands.Components;
using Content.Server.Players;
using Content.Server.Popups;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Database;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.MobState.Components;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using System.Linq;
namespace Content.Server.Chat
{
public sealed class SuicideSystem : EntitySystem
{
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
[Dependency] private readonly AdminLogSystem _adminLogSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public void Suicide(IConsoleShell shell)
{
//TODO: Make this work without the console shell
var player = shell.Player as IPlayerSession;
if (player == null)
{
shell.WriteLine(Loc.GetString("shell-cannot-run-command-from-server"));
return;
}
if (player.Status != SessionStatus.InGame || player.AttachedEntity == null)
return;
var mind = player.ContentData()?.Mind;
// This check also proves mind not-null for at the end when the mob is ghosted.
if (mind?.OwnedComponent?.Owner is not { Valid: true } owner)
{
shell.WriteLine("You don't have a mind!");
return;
}
//Checks to see if the player is dead.
if (EntityManager.TryGetComponent<MobStateComponent>(owner, out var mobState) && mobState.IsDead())
{
shell.WriteLine(Loc.GetString("suicide-command-already-dead"));
return;
}
//Checks to see if the CannotSuicide tag exits, ghosts instead.
if (_tagSystem.HasTag(owner, "CannotSuicide"))
{
if (!_gameTicker.OnGhostAttempt(mind, true))
{
shell?.WriteLine("You can't ghost right now.");
return;
}
return;
}
//TODO: needs to check if the mob is actually alive
//TODO: maybe set a suicided flag to prevent resurrection?
_adminLogSystem.Add(LogType.Suicide,
$"{EntityManager.ToPrettyString(player.AttachedEntity.Value):player} is committing suicide");
var suicideEvent = new SuicideEvent(owner);
// Held item suicide
if (EntityManager.TryGetComponent(owner, out HandsComponent handsComponent)
&& handsComponent.ActiveHandEntity is EntityUid item)
{
RaiseLocalEvent(item, suicideEvent, false);
if (suicideEvent.Handled)
{
ApplyDeath(owner, suicideEvent.Kind!.Value);
return;
}
}
// Get all entities in range of the suicider
var entities = _entityLookupSystem.GetEntitiesInRange(owner, 1, LookupFlags.Approximate | LookupFlags.Anchored).ToArray();
if (entities.Length > 0)
{
foreach (var entity in entities)
{
if (EntityManager.HasComponent<SharedItemComponent>(entity))
continue;
RaiseLocalEvent(entity, suicideEvent, false);
if (suicideEvent.Handled)
{
ApplyDeath(owner, suicideEvent.Kind!.Value);
return;
}
}
}
// Default suicide, bite your tongue
var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", owner));
owner.PopupMessageOtherClients(othersMessage);
var selfMessage = Loc.GetString("suicide-command-default-text-self");
owner.PopupMessage(selfMessage);
ApplyDeath(owner, SuicideKind.Bloodloss);
// Prevent the player from returning to the body.
// Note that mind cannot be null because otherwise owner would be null.
_gameTicker.OnGhostAttempt(mind!, false);
}
private void ApplyDeath(EntityUid target, SuicideKind kind)
{
if (kind == SuicideKind.Special) return;
// TODO SUICIDE ..heh.. anyway, someone should fix this mess.
DamageSpecifier damage = new(kind switch
{
SuicideKind.Blunt => _prototypeManager.Index<DamageTypePrototype>("Blunt"),
SuicideKind.Slash => _prototypeManager.Index<DamageTypePrototype>("Slash"),
SuicideKind.Piercing => _prototypeManager.Index<DamageTypePrototype>("Piercing"),
SuicideKind.Heat => _prototypeManager.Index<DamageTypePrototype>("Heat"),
SuicideKind.Shock => _prototypeManager.Index<DamageTypePrototype>("Shock"),
SuicideKind.Cold => _prototypeManager.Index<DamageTypePrototype>("Cold"),
SuicideKind.Poison => _prototypeManager.Index<DamageTypePrototype>("Poison"),
SuicideKind.Radiation => _prototypeManager.Index<DamageTypePrototype>("Radiation"),
SuicideKind.Asphyxiation => _prototypeManager.Index<DamageTypePrototype>("Asphyxiation"),
SuicideKind.Bloodloss => _prototypeManager.Index<DamageTypePrototype>("Bloodloss"),
_ => _prototypeManager.Index<DamageTypePrototype>("Blunt")
},
200);
_damageableSystem.TryChangeDamage(target, damage, true);
}
}
}

View File

@@ -1,19 +1,11 @@
using Content.Server.Act;
using Content.Server.Chat.Managers;
using Content.Server.Kitchen.EntitySystems;
using Content.Server.Popups;
using Content.Shared.DragDrop;
using Content.Shared.Kitchen.Components;
using Content.Shared.Popups;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using System.Threading;
namespace Content.Server.Kitchen.Components
{
[RegisterComponent, Friend(typeof(KitchenSpikeSystem))]
public sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent, ISuicideAct
public sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent
{
public List<string>? PrototypesToSpawn;
@@ -30,17 +22,5 @@ namespace Content.Server.Kitchen.Components
{
return true;
}
// ECS this out!, Handleable SuicideEvent?
SuicideKind ISuicideAct.Suicide(EntityUid victim, IChatManager chat)
{
var othersMessage = Loc.GetString("comp-kitchen-spike-suicide-other", ("victim", victim));
victim.PopupMessageOtherClients(othersMessage);
var selfMessage = Loc.GetString("comp-kitchen-spike-suicide-self");
victim.PopupMessage(selfMessage);
return SuicideKind.Piercing;
}
}
}

View File

@@ -25,7 +25,7 @@ using Robust.Shared.Player;
namespace Content.Server.Kitchen.Components
{
[RegisterComponent]
public sealed class MicrowaveComponent : SharedMicrowaveComponent, ISuicideAct
public sealed class MicrowaveComponent : SharedMicrowaveComponent
{
[Dependency] private readonly IEntityManager _entities = default!;
@@ -98,6 +98,12 @@ namespace Content.Server.Kitchen.Components
}
}
public void SetCookTime(uint cookTime)
{
_currentCookTimerTime = cookTime;
UIDirty = true;
}
private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage message)
{
if (!Powered || _busy)
@@ -193,7 +199,7 @@ namespace Content.Server.Kitchen.Components
// ReSharper disable once InconsistentNaming
// ReSharper disable once IdentifierTypo
private void Wzhzhzh()
public void Wzhzhzh()
{
if (!HasContents)
{
@@ -439,60 +445,10 @@ namespace Content.Server.Kitchen.Components
return true;
}
private void ClickSound()
public void ClickSound()
{
SoundSystem.Play(Filter.Pvs(Owner), _clickSound.GetSound(), Owner, AudioParams.Default.WithVolume(-2f));
}
SuicideKind ISuicideAct.Suicide(EntityUid victim, IChatManager chat)
{
var headCount = 0;
if (_entities.TryGetComponent<SharedBodyComponent?>(victim, out var body))
{
var headSlots = body.GetSlotsOfType(BodyPartType.Head);
foreach (var slot in headSlots)
{
var part = slot.Part;
if (part == null ||
!body.TryDropPart(slot, out var dropped))
{
continue;
}
foreach (var droppedPart in dropped.Values)
{
if (droppedPart.PartType != BodyPartType.Head)
{
continue;
}
Storage.Insert(droppedPart.Owner);
headCount++;
}
}
}
var othersMessage = headCount > 1
? Loc.GetString("microwave-component-suicide-multi-head-others-message", ("victim", victim))
: Loc.GetString("microwave-component-suicide-others-message", ("victim", victim));
victim.PopupMessageOtherClients(othersMessage);
var selfMessage = headCount > 1
? Loc.GetString("microwave-component-suicide-multi-head-message")
: Loc.GetString("microwave-component-suicide-message");
victim.PopupMessage(selfMessage);
_currentCookTimerTime = 10;
ClickSound();
UIDirty = true;
Wzhzhzh();
return SuicideKind.Heat;
}
}
public sealed class BeingMicrowavedEvent : HandledEntityEventArgs

View File

@@ -15,6 +15,8 @@ using System;
using Content.Shared.Storage;
using Robust.Shared.Random;
using static Content.Shared.Kitchen.Components.SharedKitchenSpikeComponent;
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
namespace Content.Server.Kitchen.EntitySystems
{
@@ -35,6 +37,20 @@ namespace Content.Server.Kitchen.EntitySystems
//DoAfter
SubscribeLocalEvent<KitchenSpikeComponent, SpikingFinishedEvent>(OnSpikingFinished);
SubscribeLocalEvent<KitchenSpikeComponent, SpikingFailEvent>(OnSpikingFail);
SubscribeLocalEvent<KitchenSpikeComponent, SuicideEvent>(OnSuicide);
}
private void OnSuicide(EntityUid uid, KitchenSpikeComponent component, SuicideEvent args)
{
if (args.Handled) return;
args.SetHandled(SuicideKind.Piercing);
var victim = args.Victim;
var othersMessage = Loc.GetString("comp-kitchen-spike-suicide-other", ("victim", victim));
victim.PopupMessageOtherClients(othersMessage);
var selfMessage = Loc.GetString("comp-kitchen-spike-suicide-self");
victim.PopupMessage(selfMessage);
}
private void OnSpikingFail(EntityUid uid, KitchenSpikeComponent component, SpikingFailEvent args)

View File

@@ -7,6 +7,10 @@ using Content.Shared.Item;
using Content.Shared.Kitchen.Components;
using Robust.Shared.Player;
using JetBrains.Annotations;
using Content.Shared.Interaction.Events;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Popups;
namespace Content.Server.Kitchen.EntitySystems
{
@@ -21,6 +25,56 @@ namespace Content.Server.Kitchen.EntitySystems
SubscribeLocalEvent<MicrowaveComponent, SolutionChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<MicrowaveComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<MicrowaveComponent, BreakageEventArgs>(OnBreak);
SubscribeLocalEvent<MicrowaveComponent, SuicideEvent>(OnSuicide);
}
private void OnSuicide(EntityUid uid, MicrowaveComponent component, SuicideEvent args)
{
if (args.Handled) return;
args.SetHandled(SuicideKind.Heat);
var victim = args.Victim;
var headCount = 0;
if (TryComp<SharedBodyComponent?>(victim, out var body))
{
var headSlots = body.GetSlotsOfType(BodyPartType.Head);
foreach (var slot in headSlots)
{
var part = slot.Part;
if (part == null ||
!body.TryDropPart(slot, out var dropped))
{
continue;
}
foreach (var droppedPart in dropped.Values)
{
if (droppedPart.PartType != BodyPartType.Head)
{
continue;
}
component.Storage.Insert(droppedPart.Owner);
headCount++;
}
}
}
var othersMessage = headCount > 1
? Loc.GetString("microwave-component-suicide-multi-head-others-message", ("victim", victim))
: Loc.GetString("microwave-component-suicide-others-message", ("victim", victim));
victim.PopupMessageOtherClients(othersMessage);
var selfMessage = headCount > 1
? Loc.GetString("microwave-component-suicide-multi-head-message")
: Loc.GetString("microwave-component-suicide-message");
victim.PopupMessage(selfMessage);
component.ClickSound();
component.SetCookTime(10);
component.Wzhzhzh();
}
private void OnSolutionChange(EntityUid uid, MicrowaveComponent component, SolutionChangedEvent args)

View File

@@ -22,7 +22,7 @@ namespace Content.Server.Morgue.Components
[ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(IStorageComponent))]
#pragma warning disable 618
public sealed class CrematoriumEntityStorageComponent : MorgueEntityStorageComponent, ISuicideAct
public sealed class CrematoriumEntityStorageComponent : MorgueEntityStorageComponent
#pragma warning restore 618
{
[Dependency] private readonly IEntityManager _entities = default!;
@@ -100,34 +100,5 @@ namespace Content.Server.Morgue.Components
}, _cremateCancelToken.Token);
}
SuicideKind ISuicideAct.Suicide(EntityUid victim, IChatManager chat)
{
if (_entities.TryGetComponent(victim, out ActorComponent? actor) && actor.PlayerSession.ContentData()?.Mind is {} mind)
{
EntitySystem.Get<GameTicker>().OnGhostAttempt(mind, false);
if (mind.OwnedEntity is {Valid: true} entity)
{
entity.PopupMessage(Loc.GetString("crematorium-entity-storage-component-suicide-message"));
}
}
victim.PopupMessageOtherClients(Loc.GetString("crematorium-entity-storage-component-suicide-message-others", ("victim", victim)));
if (CanInsert(victim))
{
Insert(victim);
EntitySystem.Get<StandingStateSystem>().Down(victim, false);
}
else
{
_entities.DeleteEntity(victim);
}
Cremate();
return SuicideKind.Heat;
}
}
}

View File

@@ -4,12 +4,22 @@ using Content.Shared.Examine;
using Content.Shared.Database;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Content.Shared.Interaction.Events;
using Robust.Server.GameObjects;
using Content.Server.Players;
using Content.Server.GameTicking;
using Content.Server.Popups;
using Content.Shared.Standing;
using Robust.Shared.Player;
namespace Content.Server.Morgue
{
[UsedImplicitly]
public sealed class MorgueSystem : EntitySystem
{
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly StandingStateSystem _stando = default!;
private float _accumulatedFrameTime;
@@ -19,9 +29,44 @@ namespace Content.Server.Morgue
SubscribeLocalEvent<CrematoriumEntityStorageComponent, GetVerbsEvent<AlternativeVerb>>(AddCremateVerb);
SubscribeLocalEvent<CrematoriumEntityStorageComponent, ExaminedEvent>(OnCrematoriumExamined);
SubscribeLocalEvent<CrematoriumEntityStorageComponent, SuicideEvent>(OnSuicide);
SubscribeLocalEvent<MorgueEntityStorageComponent, ExaminedEvent>(OnMorgueExamined);
}
private void OnSuicide(EntityUid uid, CrematoriumEntityStorageComponent component, SuicideEvent args)
{
if (args.Handled) return;
args.SetHandled(SuicideKind.Heat);
var victim = args.Victim;
if (TryComp(victim, out ActorComponent? actor) && actor.PlayerSession.ContentData()?.Mind is { } mind)
{
_ticker.OnGhostAttempt(mind, false);
if (mind.OwnedEntity is { Valid: true } entity)
{
_popup.PopupEntity(Loc.GetString("crematorium-entity-storage-component-suicide-message"), entity, Filter.Pvs(entity, entityManager: EntityManager));
}
}
_popup.PopupEntity(
Loc.GetString("crematorium-entity-storage-component-suicide-message-others", ("victim", victim)),
victim,
Filter.Pvs(victim, entityManager: EntityManager).RemoveWhereAttachedEntity(e => e == victim));
if (component.CanInsert(victim))
{
component.Insert(victim);
_stando.Down(victim, false);
}
else
{
EntityManager.DeleteEntity(victim);
}
component.Cremate();
}
private void AddCremateVerb(EntityUid uid, CrematoriumEntityStorageComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract || component.Cooking || component.Open)

View File

@@ -14,7 +14,7 @@ namespace Content.Server.Recycling.Components
// TODO: Add sound and safe beep
[RegisterComponent]
[Friend(typeof(RecyclerSystem))]
public sealed class RecyclerComponent : Component, ISuicideAct
public sealed class RecyclerComponent : Component
{
[Dependency] private readonly IEntityManager _entMan = default!;
@@ -44,27 +44,6 @@ namespace Content.Server.Recycling.Components
}
}
SuicideKind ISuicideAct.Suicide(EntityUid victim, IChatManager chat)
{
if (_entMan.TryGetComponent(victim, out ActorComponent? actor) &&
actor.PlayerSession.ContentData()?.Mind is { } mind)
{
EntitySystem.Get<GameTicker>().OnGhostAttempt(mind, false);
mind.OwnedEntity?.PopupMessage(Loc.GetString("recycler-component-suicide-message"));
}
victim.PopupMessageOtherClients(Loc.GetString("recycler-component-suicide-message-others",
("victim", victim)));
if (_entMan.TryGetComponent<SharedBodyComponent?>(victim, out var body))
{
body.Gib(true);
}
EntitySystem.Get<RecyclerSystem>().Bloodstain(this);
return SuicideKind.Bloodloss;
}
/// <summary>
/// Default sound to play when recycling
/// </summary>

View File

@@ -1,11 +1,17 @@
using Content.Server.Audio;
using Content.Server.GameTicking;
using Content.Server.Players;
using Content.Server.Popups;
using Content.Server.Power.Components;
using Content.Server.Recycling.Components;
using Content.Shared.Audio;
using Content.Shared.Body.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
using Content.Shared.Recycling;
using Content.Shared.Tag;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Player;
@@ -17,6 +23,8 @@ namespace Content.Server.Recycling
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly AmbientSoundSystem _ambience = default!;
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly TagSystem _tags = default!;
private const float RecyclerSoundCooldown = 0.8f;
@@ -25,6 +33,34 @@ namespace Content.Server.Recycling
{
SubscribeLocalEvent<RecyclerComponent, StartCollideEvent>(OnCollide);
SubscribeLocalEvent<RecyclerComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<RecyclerComponent, SuicideEvent>(OnSuicide);
}
private void OnSuicide(EntityUid uid, RecyclerComponent component, SuicideEvent args)
{
if (args.Handled) return;
args.SetHandled(SuicideKind.Bloodloss);
var victim = args.Victim;
if (TryComp(victim, out ActorComponent? actor) &&
actor.PlayerSession.ContentData()?.Mind is { } mind)
{
_ticker.OnGhostAttempt(mind, false);
if (mind.OwnedEntity is { Valid: true } entity)
{
_popup.PopupEntity(Loc.GetString("recycler-component-suicide-message"), entity, Filter.Pvs(entity, entityManager: EntityManager));
}
}
_popup.PopupEntity(Loc.GetString("recycler-component-suicide-message-others", ("victim", victim)),
victim,
Filter.Pvs(victim, entityManager: EntityManager).RemoveWhereAttachedEntity(e => e == victim));
if (TryComp<SharedBodyComponent?>(victim, out var body))
{
body.Gib(true);
}
Bloodstain(component);
}
public void EnableRecycler(RecyclerComponent component)

View File

@@ -1,15 +1,11 @@
using Content.Server.Act;
using Content.Server.Chat.Managers;
using Content.Shared.Sound;
using Content.Shared.Tools;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Toilet
{
[RegisterComponent]
public sealed class ToiletComponent : Component, ISuicideAct
public sealed class ToiletComponent : Component
{
[DataField("pryLidTime")]
public float PryLidTime = 1f;
@@ -23,12 +19,5 @@ namespace Content.Server.Toilet
public bool LidOpen = false;
public bool IsSeatUp = false;
public bool IsPrying = false;
// todo: move me to ECS
SuicideKind ISuicideAct.Suicide(EntityUid victim, IChatManager chat)
{
return EntitySystem.Get<ToiletSystem>().Suicide(Owner, victim, this);
}
}
}

View File

@@ -9,6 +9,7 @@ using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Toilet;
using Content.Shared.Tools.Components;
using Robust.Shared.Audio;
@@ -35,10 +36,16 @@ namespace Content.Server.Toilet
SubscribeLocalEvent<ToiletComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<ToiletComponent, InteractHandEvent>(OnInteractHand);
SubscribeLocalEvent<ToiletComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<ToiletComponent, SuicideEvent>(OnSuicide);
SubscribeLocalEvent<ToiletPryFinished>(OnToiletPried);
SubscribeLocalEvent<ToiletPryInterrupted>(OnToiletInterrupt);
}
private void OnSuicide(EntityUid uid, ToiletComponent component, SuicideEvent args)
{
Suicide(component.Owner, uid, component);
}
private void OnInit(EntityUid uid, ToiletComponent component, ComponentInit args)
{
EntityManager.EnsureComponent<SecretStashComponent>(uid);

View File

@@ -0,0 +1,39 @@
namespace Content.Shared.Interaction.Events
{
/// <summary>
/// Raised Directed at an entity to check whether they will handle the suicide.
/// </summary>
public sealed class SuicideEvent : EntityEventArgs
{
public SuicideEvent(EntityUid victim)
{
Victim = victim;
}
public void SetHandled(SuicideKind kind)
{
if (Handled) throw new InvalidOperationException("Suicide was already handled");
Kind = kind;
}
public SuicideKind? Kind { get; private set; }
public EntityUid Victim { get; private set; }
public bool Handled => Kind != null;
}
public enum SuicideKind
{
Special, //Doesn't damage the mob, used for "weird" suicides like gibbing
//Damage type suicides
Blunt,
Slash,
Piercing,
Heat,
Shock,
Cold,
Poison,
Radiation,
Asphyxiation,
Bloodloss
}
}