| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- using System.Linq;
- using Content.Shared.Damage;
- using Content.Shared.Damage.Prototypes;
- using Content.Shared.Execution;
- using Content.Shared.FixedPoint;
- using Content.Shared.Ghost;
- using Content.Shared.Hands.Components;
- using Content.Shared.Hands.EntitySystems;
- using Content.Shared.Mind;
- using Content.Shared.Mobs.Components;
- using Content.Shared.Mobs.Systems;
- using Content.Shared.Tag;
- using Robust.Server.GameObjects;
- using Robust.Server.Player;
- using Robust.Shared.Console;
- using Robust.Shared.GameObjects;
- using Robust.Shared.Prototypes;
- namespace Content.IntegrationTests.Tests.Commands;
- [TestFixture]
- public sealed class SuicideCommandTests
- {
- [TestPrototypes]
- private const string Prototypes = @"
- - type: entity
- id: SharpTestObject
- name: very sharp test object
- components:
- - type: Item
- - type: MeleeWeapon
- damage:
- types:
- Slash: 5
- - type: Execution
- - type: entity
- id: MixedDamageTestObject
- name: mixed damage test object
- components:
- - type: Item
- - type: MeleeWeapon
- damage:
- types:
- Slash: 5
- Blunt: 5
- - type: Execution
- - type: entity
- id: TestMaterialReclaimer
- name: test version of the material reclaimer
- components:
- - type: MaterialReclaimer";
- /// <summary>
- /// Run the suicide command in the console
- /// Should successfully kill the player and ghost them
- /// </summary>
- [Test]
- public async Task TestSuicide()
- {
- await using var pair = await PoolManager.GetServerClient(new PoolSettings
- {
- Connected = true,
- Dirty = true,
- DummyTicker = false
- });
- var server = pair.Server;
- var consoleHost = server.ResolveDependency<IConsoleHost>();
- var entManager = server.ResolveDependency<IEntityManager>();
- var playerMan = server.ResolveDependency<IPlayerManager>();
- var mindSystem = entManager.System<SharedMindSystem>();
- var mobStateSystem = entManager.System<MobStateSystem>();
- // We need to know the player and whether they can be hurt, killed, and whether they have a mind
- var player = playerMan.Sessions.First().AttachedEntity!.Value;
- var mind = mindSystem.GetMind(player);
- MindComponent mindComponent = default;
- MobStateComponent mobStateComp = default;
- await server.WaitPost(() =>
- {
- if (mind != null)
- mindComponent = entManager.GetComponent<MindComponent>(mind.Value);
- mobStateComp = entManager.GetComponent<MobStateComponent>(player);
- });
- // Check that running the suicide command kills the player
- // and properly ghosts them without them being able to return to their body
- await server.WaitAssertion(() =>
- {
- consoleHost.GetSessionShell(playerMan.Sessions.First()).ExecuteCommand("suicide");
- Assert.Multiple(() =>
- {
- Assert.That(mobStateSystem.IsDead(player, mobStateComp));
- Assert.That(entManager.TryGetComponent<GhostComponent>(mindComponent.CurrentEntity, out var ghostComp) &&
- !ghostComp.CanReturnToBody);
- });
- });
- await pair.CleanReturnAsync();
- }
- /// <summary>
- /// Run the suicide command while the player is already injured
- /// This should only deal as much damage as necessary to get to the dead threshold
- /// </summary>
- [Test]
- public async Task TestSuicideWhileDamaged()
- {
- await using var pair = await PoolManager.GetServerClient(new PoolSettings
- {
- Connected = true,
- Dirty = true,
- DummyTicker = false
- });
- var server = pair.Server;
- var consoleHost = server.ResolveDependency<IConsoleHost>();
- var entManager = server.ResolveDependency<IEntityManager>();
- var playerMan = server.ResolveDependency<IPlayerManager>();
- var protoMan = server.ResolveDependency<IPrototypeManager>();
- var damageableSystem = entManager.System<DamageableSystem>();
- var mindSystem = entManager.System<SharedMindSystem>();
- var mobStateSystem = entManager.System<MobStateSystem>();
- // We need to know the player and whether they can be hurt, killed, and whether they have a mind
- var player = playerMan.Sessions.First().AttachedEntity!.Value;
- var mind = mindSystem.GetMind(player);
- MindComponent mindComponent = default;
- MobStateComponent mobStateComp = default;
- MobThresholdsComponent mobThresholdsComp = default;
- DamageableComponent damageableComp = default;
- await server.WaitPost(() =>
- {
- if (mind != null)
- mindComponent = entManager.GetComponent<MindComponent>(mind.Value);
- mobStateComp = entManager.GetComponent<MobStateComponent>(player);
- mobThresholdsComp = entManager.GetComponent<MobThresholdsComponent>(player);
- damageableComp = entManager.GetComponent<DamageableComponent>(player);
- if (protoMan.TryIndex<DamageTypePrototype>("Slash", out var slashProto))
- damageableSystem.TryChangeDamage(player, new DamageSpecifier(slashProto, FixedPoint2.New(46.5)));
- });
- // Check that running the suicide command kills the player
- // and properly ghosts them without them being able to return to their body
- // and that all the damage is concentrated in the Slash category
- await server.WaitAssertion(() =>
- {
- consoleHost.GetSessionShell(playerMan.Sessions.First()).ExecuteCommand("suicide");
- var lethalDamageThreshold = mobThresholdsComp.Thresholds.Keys.Last();
- Assert.Multiple(() =>
- {
- Assert.That(mobStateSystem.IsDead(player, mobStateComp));
- Assert.That(entManager.TryGetComponent<GhostComponent>(mindComponent.CurrentEntity, out var ghostComp) &&
- !ghostComp.CanReturnToBody);
- Assert.That(damageableComp.Damage.GetTotal(), Is.EqualTo(lethalDamageThreshold));
- });
- });
- await pair.CleanReturnAsync();
- }
- /// <summary>
- /// Run the suicide command in the console
- /// Should only ghost the player but not kill them
- /// </summary>
- [Test]
- public async Task TestSuicideWhenCannotSuicide()
- {
- await using var pair = await PoolManager.GetServerClient(new PoolSettings
- {
- Connected = true,
- Dirty = true,
- DummyTicker = false
- });
- var server = pair.Server;
- var consoleHost = server.ResolveDependency<IConsoleHost>();
- var entManager = server.ResolveDependency<IEntityManager>();
- var playerMan = server.ResolveDependency<IPlayerManager>();
- var mindSystem = entManager.System<SharedMindSystem>();
- var mobStateSystem = entManager.System<MobStateSystem>();
- var tagSystem = entManager.System<TagSystem>();
- // We need to know the player and whether they can be hurt, killed, and whether they have a mind
- var player = playerMan.Sessions.First().AttachedEntity!.Value;
- var mind = mindSystem.GetMind(player);
- MindComponent mindComponent = default;
- MobStateComponent mobStateComp = default;
- await server.WaitPost(() =>
- {
- if (mind != null)
- mindComponent = entManager.GetComponent<MindComponent>(mind.Value);
- mobStateComp = entManager.GetComponent<MobStateComponent>(player);
- });
- tagSystem.AddTag(player, "CannotSuicide");
- // Check that running the suicide command kills the player
- // and properly ghosts them without them being able to return to their body
- await server.WaitAssertion(() =>
- {
- consoleHost.GetSessionShell(playerMan.Sessions.First()).ExecuteCommand("suicide");
- Assert.Multiple(() =>
- {
- Assert.That(mobStateSystem.IsAlive(player, mobStateComp));
- Assert.That(entManager.TryGetComponent<GhostComponent>(mindComponent.CurrentEntity, out var ghostComp) &&
- !ghostComp.CanReturnToBody);
- });
- });
- await pair.CleanReturnAsync();
- }
- /// <summary>
- /// Run the suicide command while the player is holding an execution-capable weapon
- /// </summary>
- [Test]
- public async Task TestSuicideByHeldItem()
- {
- await using var pair = await PoolManager.GetServerClient(new PoolSettings
- {
- Connected = true,
- Dirty = true,
- DummyTicker = false
- });
- var server = pair.Server;
- var consoleHost = server.ResolveDependency<IConsoleHost>();
- var entManager = server.ResolveDependency<IEntityManager>();
- var playerMan = server.ResolveDependency<IPlayerManager>();
- var handsSystem = entManager.System<SharedHandsSystem>();
- var mindSystem = entManager.System<SharedMindSystem>();
- var mobStateSystem = entManager.System<MobStateSystem>();
- var transformSystem = entManager.System<TransformSystem>();
- var damageableSystem = entManager.System<DamageableSystem>();
- // We need to know the player and whether they can be hurt, killed, and whether they have a mind
- var player = playerMan.Sessions.First().AttachedEntity!.Value;
- var mind = mindSystem.GetMind(player);
- MindComponent mindComponent = default;
- MobStateComponent mobStateComp = default;
- MobThresholdsComponent mobThresholdsComp = default;
- DamageableComponent damageableComp = default;
- HandsComponent handsComponent = default;
- await server.WaitPost(() =>
- {
- if (mind != null)
- mindComponent = entManager.GetComponent<MindComponent>(mind.Value);
- mobStateComp = entManager.GetComponent<MobStateComponent>(player);
- mobThresholdsComp = entManager.GetComponent<MobThresholdsComponent>(player);
- damageableComp = entManager.GetComponent<DamageableComponent>(player);
- handsComponent = entManager.GetComponent<HandsComponent>(player);
- });
- // Spawn the weapon of choice and put it in the player's hands
- await server.WaitPost(() =>
- {
- var item = entManager.SpawnEntity("SharpTestObject", transformSystem.GetMapCoordinates(player));
- Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHand!));
- entManager.TryGetComponent<ExecutionComponent>(item, out var executionComponent);
- Assert.That(executionComponent, Is.Not.EqualTo(null));
- });
- // Check that running the suicide command kills the player
- // and properly ghosts them without them being able to return to their body
- // and that all the damage is concentrated in the Slash category
- await server.WaitAssertion(() =>
- {
- // Heal all damage first (possible low pressure damage taken)
- damageableSystem.SetAllDamage(player, damageableComp, 0);
- consoleHost.GetSessionShell(playerMan.Sessions.First()).ExecuteCommand("suicide");
- var lethalDamageThreshold = mobThresholdsComp.Thresholds.Keys.Last();
- Assert.Multiple(() =>
- {
- Assert.That(mobStateSystem.IsDead(player, mobStateComp));
- Assert.That(entManager.TryGetComponent<GhostComponent>(mindComponent.CurrentEntity, out var ghostComp) &&
- !ghostComp.CanReturnToBody);
- Assert.That(damageableComp.Damage.DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold));
- });
- });
- await pair.CleanReturnAsync();
- }
- /// <summary>
- /// Run the suicide command while the player is holding an execution-capable weapon
- /// with damage spread between slash and blunt
- /// </summary>
- [Test]
- public async Task TestSuicideByHeldItemSpreadDamage()
- {
- await using var pair = await PoolManager.GetServerClient(new PoolSettings
- {
- Connected = true,
- Dirty = true,
- DummyTicker = false
- });
- var server = pair.Server;
- var consoleHost = server.ResolveDependency<IConsoleHost>();
- var entManager = server.ResolveDependency<IEntityManager>();
- var playerMan = server.ResolveDependency<IPlayerManager>();
- var handsSystem = entManager.System<SharedHandsSystem>();
- var mindSystem = entManager.System<SharedMindSystem>();
- var mobStateSystem = entManager.System<MobStateSystem>();
- var transformSystem = entManager.System<TransformSystem>();
- var damageableSystem = entManager.System<DamageableSystem>();
- // We need to know the player and whether they can be hurt, killed, and whether they have a mind
- var player = playerMan.Sessions.First().AttachedEntity!.Value;
- var mind = mindSystem.GetMind(player);
- MindComponent mindComponent = default;
- MobStateComponent mobStateComp = default;
- MobThresholdsComponent mobThresholdsComp = default;
- DamageableComponent damageableComp = default;
- HandsComponent handsComponent = default;
- await server.WaitPost(() =>
- {
- if (mind != null)
- mindComponent = entManager.GetComponent<MindComponent>(mind.Value);
- mobStateComp = entManager.GetComponent<MobStateComponent>(player);
- mobThresholdsComp = entManager.GetComponent<MobThresholdsComponent>(player);
- damageableComp = entManager.GetComponent<DamageableComponent>(player);
- handsComponent = entManager.GetComponent<HandsComponent>(player);
- });
- // Spawn the weapon of choice and put it in the player's hands
- await server.WaitPost(() =>
- {
- var item = entManager.SpawnEntity("MixedDamageTestObject", transformSystem.GetMapCoordinates(player));
- Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHand!));
- entManager.TryGetComponent<ExecutionComponent>(item, out var executionComponent);
- Assert.That(executionComponent, Is.Not.EqualTo(null));
- });
- // Check that running the suicide command kills the player
- // and properly ghosts them without them being able to return to their body
- // and that slash damage is split in half
- await server.WaitAssertion(() =>
- {
- // Heal all damage first (possible low pressure damage taken)
- damageableSystem.SetAllDamage(player, damageableComp, 0);
- consoleHost.GetSessionShell(playerMan.Sessions.First()).ExecuteCommand("suicide");
- var lethalDamageThreshold = mobThresholdsComp.Thresholds.Keys.Last();
- Assert.Multiple(() =>
- {
- Assert.That(mobStateSystem.IsDead(player, mobStateComp));
- Assert.That(entManager.TryGetComponent<GhostComponent>(mindComponent.CurrentEntity, out var ghostComp) &&
- !ghostComp.CanReturnToBody);
- Assert.That(damageableComp.Damage.DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold / 2));
- });
- });
- await pair.CleanReturnAsync();
- }
- }
|