-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* melee executions * fix damage bug * cleanup * address reviews hopefully * resistance bypass mechanic * component changes * self executions (not finished yet) * self execs part two * ok i fixed things (still not finished) * finish everything * review stuff * nuke if (kind = special) * more review stuffs * Make suicide system much less hardcoded and make much more use of events * Fix a dumb bug I introduced * self execution popups * Integration tests * Why did they even take 0.5 blunt damage? * More consistent integration tests * Destructive equals true * Allow it to dirty-dispose * IS THIS WHAT YOU WANT? * FRESH AND CLEAN * modifier to multiplier * don't jinx the integration tests * no file-scoped namespace * Move the rest of execution to shared, create SuicideGhostEvent * handled * Get rid of unused code and add a comment * ghost before suicide * stop cat suicides * popup fix + small suicide change * make it a bit better --------- Co-authored-by: Plykiya <[email protected]>
- Loading branch information
1 parent
c25c5ec
commit 220aff2
Showing
26 changed files
with
1,030 additions
and
201 deletions.
There are no files selected for viewing
365 changes: 365 additions & 0 deletions
365
Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,365 @@ | ||
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>(); | ||
|
||
// 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(() => | ||
{ | ||
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>(); | ||
|
||
// 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(() => | ||
{ | ||
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(); | ||
} | ||
} |
Oops, something went wrong.