* Fix toilet suicide

* Fix ghost ghosting

* Clean suicide system
This commit is contained in:
wrexbe
2022-05-12 16:09:15 -07:00
committed by GitHub
parent f8f5a7fbf3
commit 3467a83d97
4 changed files with 125 additions and 154 deletions

View File

@@ -1,35 +1,15 @@
using System.Linq;
using Content.Server.Act;
using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Hands.Components;
using Content.Server.Players; using Content.Server.Players;
using Content.Server.Popups;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Database;
using Content.Shared.Item;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Content.Shared.MobState.Components;
namespace Content.Server.Chat.Commands namespace Content.Server.Chat.Commands
{ {
[AnyCommand] [AnyCommand]
internal sealed class SuicideCommand : IConsoleCommand internal sealed class SuicideCommand : IConsoleCommand
{ {
[Dependency] private readonly IEntityManager _entities = default!;
public string Command => "suicide"; public string Command => "suicide";
public string Description => Loc.GetString("suicide-command-description"); public string Description => Loc.GetString("suicide-command-description");
@@ -38,7 +18,37 @@ namespace Content.Server.Chat.Commands
public void Execute(IConsoleShell shell, string argStr, string[] args) public void Execute(IConsoleShell shell, string argStr, string[] args)
{ {
EntitySystem.Get<SuicideSystem>().Suicide(shell); if (shell.Player is not IPlayerSession player)
{
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 } victim)
{
shell.WriteLine("You don't have a mind!");
return;
}
var gameTicker = EntitySystem.Get<GameTicker>();
var suicideSystem = EntitySystem.Get<SuicideSystem>();
if (suicideSystem.Suicide(victim))
{
// Prevent the player from returning to the body.
// Note that mind cannot be null because otherwise victim would be null.
gameTicker.OnGhostAttempt(mind!, false);
return;
}
if (gameTicker.OnGhostAttempt(mind, true))
return;
shell?.WriteLine("You can't ghost right now.");
} }
} }
} }

View File

@@ -1,9 +1,5 @@
using Content.Server.Act;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Server.Players;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
@@ -13,138 +9,107 @@ using Content.Shared.Item;
using Content.Shared.MobState.Components; using Content.Shared.MobState.Components;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Tag; using Content.Shared.Tag;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using System.Linq;
namespace Content.Server.Chat namespace Content.Server.Chat
{ {
public sealed class SuicideSystem : EntitySystem public sealed class SuicideSystem : EntitySystem
{ {
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
[Dependency] private readonly AdminLogSystem _adminLogSystem = 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!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
public void Suicide(IConsoleShell shell) public bool Suicide(EntityUid victim)
{ {
//TODO: Make this work without the console shell // Checks to see if the CannotSuicide tag exits, ghosts instead.
if (_tagSystem.HasTag(victim, "CannotSuicide"))
var player = shell.Player as IPlayerSession;
if (player == null)
{ {
shell.WriteLine(Loc.GetString("shell-cannot-run-command-from-server")); return false;
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. // Checks to see if the player is dead.
if (EntityManager.TryGetComponent<MobStateComponent>(owner, out var mobState) && mobState.IsDead()) if (!EntityManager.TryGetComponent<MobStateComponent>(victim, out var mobState) || mobState.IsDead())
{ {
shell.WriteLine(Loc.GetString("suicide-command-already-dead")); return false;
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, _adminLogSystem.Add(LogType.Suicide,
$"{EntityManager.ToPrettyString(player.AttachedEntity.Value):player} is committing suicide"); $"{EntityManager.ToPrettyString(victim):player} is committing suicide");
var suicideEvent = new SuicideEvent(owner); var suicideEvent = new SuicideEvent(victim);
// Held item suicide
if (EntityManager.TryGetComponent(owner, out HandsComponent handsComponent) // If you are critical, you wouldn't be able to use your surroundings to suicide, so you do the default suicide
if (!mobState.IsCritical())
{
EnvironmentSuicideHandler(victim, suicideEvent);
}
DefaultSuicideHandler(victim, suicideEvent);
ApplyDeath(victim, suicideEvent.Kind!.Value);
return true;
}
/// <summary>
/// If not handled, does the default suicide, which is biting your own tongue
/// </summary>
/// <param name="victim">The person attempting to die</param>
private static void DefaultSuicideHandler(EntityUid victim, SuicideEvent suicideEvent)
{
if (suicideEvent.Handled) return;
var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", victim));
victim.PopupMessageOtherClients(othersMessage);
var selfMessage = Loc.GetString("suicide-command-default-text-self");
victim.PopupMessage(selfMessage);
suicideEvent.SetHandled(SuicideKind.Bloodloss);
}
/// <summary>
/// Raise event to attempt to use held item, or surrounding entities to commit suicide
/// </summary>
/// <param name="victim">The person attempting to die</param>
private void EnvironmentSuicideHandler(EntityUid victim, SuicideEvent suicideEvent)
{
// Suicide by held item
if (EntityManager.TryGetComponent(victim, out HandsComponent handsComponent)
&& handsComponent.ActiveHandEntity is EntityUid item) && handsComponent.ActiveHandEntity is EntityUid item)
{ {
RaiseLocalEvent(item, suicideEvent, false); RaiseLocalEvent(item, suicideEvent, false);
if (suicideEvent.Handled) if (suicideEvent.Handled)
{
ApplyDeath(owner, suicideEvent.Kind!.Value);
return; return;
} }
}
// Get all entities in range of the suicider // Suicide by nearby entity (ex: Microwave)
var entities = _entityLookupSystem.GetEntitiesInRange(owner, 1, LookupFlags.Approximate | LookupFlags.Anchored).ToArray(); foreach (var entity in _entityLookupSystem.GetEntitiesInRange(victim, 1, LookupFlags.Approximate | LookupFlags.Anchored))
if (entities.Length > 0)
{
foreach (var entity in entities)
{ {
// Skip any nearby items that can be picked up, we already checked the active held item above
if (EntityManager.HasComponent<SharedItemComponent>(entity)) if (EntityManager.HasComponent<SharedItemComponent>(entity))
continue; continue;
RaiseLocalEvent(entity, suicideEvent, false); RaiseLocalEvent(entity, suicideEvent, false);
if (suicideEvent.Handled) if (suicideEvent.Handled)
{ break;
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) private void ApplyDeath(EntityUid target, SuicideKind kind)
{ {
if (kind == SuicideKind.Special) return; if (kind == SuicideKind.Special)
// TODO SUICIDE ..heh.. anyway, someone should fix this mess. return;
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); if (!_prototypeManager.TryIndex<DamageTypePrototype>(kind.ToString(), out var damagePrototype))
{
const SuicideKind fallback = SuicideKind.Blunt;
Logger.Error(
$"{nameof(SuicideSystem)} could not find the damage type prototype associated with {kind}. Falling back to {fallback}");
damagePrototype = _prototypeManager.Index<DamageTypePrototype>(fallback.ToString());
}
const int lethalAmountOfDamage = 200; // TODO: Would be nice to get this number from somewhere else
_damageableSystem.TryChangeDamage(target, new(damagePrototype, lethalAmountOfDamage), true);
} }
} }
} }

View File

@@ -154,7 +154,7 @@ namespace Content.Server.GameTicking
if (handleEv.Handled) if (handleEv.Handled)
return handleEv.Result; return handleEv.Result;
var playerEntity = mind.OwnedEntity; var playerEntity = mind.CurrentEntity;
var entities = IoCManager.Resolve<IEntityManager>(); var entities = IoCManager.Resolve<IEntityManager>();
if (entities.HasComponent<GhostComponent>(playerEntity)) if (entities.HasComponent<GhostComponent>(playerEntity))

View File

@@ -43,7 +43,34 @@ namespace Content.Server.Toilet
private void OnSuicide(EntityUid uid, ToiletComponent component, SuicideEvent args) private void OnSuicide(EntityUid uid, ToiletComponent component, SuicideEvent args)
{ {
Suicide(component.Owner, uid, component); if (args.Handled) return;
// Check that victim has a head
if (EntityManager.TryGetComponent<SharedBodyComponent>(args.Victim, out var body) &&
body.HasPartOfType(BodyPartType.Head))
{
var othersMessage = Loc.GetString("toilet-component-suicide-head-message-others",
("victim", args.Victim), ("owner", uid));
_popupSystem.PopupEntity(othersMessage, uid, Filter.Pvs(args.Victim).RemoveWhereAttachedEntity(puid => puid == args.Victim));
var selfMessage = Loc.GetString("toilet-component-suicide-head-message",
("owner", uid));
_popupSystem.PopupEntity(selfMessage, uid, Filter.Entities(args.Victim));
args.SetHandled(SuicideKind.Asphyxiation);
}
else
{
var othersMessage = Loc.GetString("toilet-component-suicide-message-others",
("victim", args.Victim), ("owner", uid));
_popupSystem.PopupEntity(othersMessage, uid, Filter.Pvs(uid).RemoveWhereAttachedEntity(puid => puid == args.Victim));
var selfMessage = Loc.GetString("toilet-component-suicide-message",
("owner", uid));
_popupSystem.PopupEntity(selfMessage, uid, Filter.Entities(args.Victim));
args.SetHandled(SuicideKind.Blunt);
}
} }
private void OnInit(EntityUid uid, ToiletComponent component, ComponentInit args) private void OnInit(EntityUid uid, ToiletComponent component, ComponentInit args)
@@ -130,37 +157,6 @@ namespace Content.Server.Toilet
} }
} }
public SuicideKind Suicide(EntityUid uid, EntityUid victimUid, ToiletComponent? component = null,
MetaDataComponent? meta = null, MetaDataComponent? victimMeta = null)
{
// check that victim even have head
if (EntityManager.TryGetComponent<SharedBodyComponent>(victimUid, out var body) &&
body.HasPartOfType(BodyPartType.Head))
{
var othersMessage = Loc.GetString("toilet-component-suicide-head-message-others",
("victim", victimUid),("owner", uid));
_popupSystem.PopupEntity(othersMessage, uid, Filter.Pvs(victimUid).RemoveWhereAttachedEntity(puid => puid == victimUid));
var selfMessage = Loc.GetString("toilet-component-suicide-head-message",
("owner", uid));
_popupSystem.PopupEntity(selfMessage, uid, Filter.Entities(victimUid));
return SuicideKind.Asphyxiation;
}
else
{
var othersMessage = Loc.GetString("toilet-component-suicide-message-others",
("victim", victimUid),("owner", uid));
_popupSystem.PopupEntity(othersMessage, uid, Filter.Pvs(uid).RemoveWhereAttachedEntity(puid => puid == victimUid));
var selfMessage = Loc.GetString("toilet-component-suicide-message",
("owner", uid));
_popupSystem.PopupEntity(selfMessage, uid, Filter.Entities(victimUid));
return SuicideKind.Blunt;
}
}
private void OnToiletInterrupt(ToiletPryInterrupted ev) private void OnToiletInterrupt(ToiletPryInterrupted ev)
{ {
if (!EntityManager.TryGetComponent(ev.Uid, out ToiletComponent? toilet)) if (!EntityManager.TryGetComponent(ev.Uid, out ToiletComponent? toilet))