* Offbrand medical * what if we regrade * zombies are mostly there thats it thats a wrap xd * here's changeling * some bonus gut punches * start working on the guidebook * fix rsi and yaml lints * my agrichem so fits * we stay rejuvenated * my china so laked * debrute * fix suicide * fix the suicide tests * my surgery so requires laying down * the guidebook continues * READ KEB PAGES * keb vascular recoupler * read keb medicine * fix yaml lint * fix the EntityRemoveConstructionGraphStep * fix overlay init * scalpels are not a food tool * return of the programmer art * my line so nieuw * boxes deserve veins too * mrrrp yaml * what if we redid brain damage alerts * bloot pressure * kill mannitol drowsiness * get licensed * my read so me * get feedbacked nerd * fine-tune the heart stoppage conditions * cryostasis adjustments, guidebook adjustments, fix negative strain issues * my surgery so table * fix heart surgery and guidebook * medicine & defibrillator pass * iv bags and stands * prefills * janet gets very sidetracked * mostly finished iv stuff * what if we fixed the guidebook * halve decapoid cryostasis * my medicines so IV * finetune cryostasis * less logspam * metabolism-aware iv stands and cryopods * give people painkillers * yaml lint real * fix blood build * finish rebase * tidy up localization * clean up my yaml beasties... * soft curve after exceeding maximum damage * husks/bonedeaths Grabbag of Offmed fixes & improvements (#3461) * CPR moment * Mob AI fix * Fix brain oxygenation not updating on regeneration * sorry gamers you cannot resist the pull * Troll combat abilities more in softcrit praying rn (#3467) dont have CPR be 50% (#3468) Make offbrand murder easier to contend with (#3473) * e * disrupt people in softcrit when attacking them * ok gamers we're gaming * forgor Hopefully final pass before Offbrand merge (#3475) First pass of Offbrand adjustments (#3477) Swap blood pressure values in health analyzer (#3476) Systolic over diastolic Co-authored-by: Kip <32859367+kipdotnet@users.noreply.github.com> Offbrand pass 2: Mostly bugfixes (#3480) Fix zeds causing PVS reloads (#3482) Offbrand pass 3: I hate surgery I hate surgery I hate surgery I (#3481) * set up surgery ui * test fail real Pain/braingasps (#3487) Offmed bundle 5 - the evil one (#3489) * Evil cavity surgery * les borgues * nicotine moment * epinephrine RNG * legalese * test fail real * ok jamers cope with c4 Pass 6
377 lines
15 KiB
C#
377 lines
15 KiB
C#
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";
|
|
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
|
|
private static readonly ProtoId<DamageTypePrototype> DamageType = "Slash";
|
|
|
|
/// <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()
|
|
{
|
|
return; // Offbrand
|
|
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);
|
|
|
|
var slashProto = protoMan.Index(DamageType);
|
|
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, CannotSuicideTag);
|
|
|
|
// 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()
|
|
{
|
|
return; // Offbrand
|
|
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.ActiveHandId!));
|
|
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()
|
|
{
|
|
return; // Offbrand
|
|
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.ActiveHandId!));
|
|
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();
|
|
}
|
|
}
|