Add chasm integration tests (#40286)
* add chasm integration test * fix assert * fix * more fixes * review
This commit is contained in:
144
Content.IntegrationTests/Tests/Chasm/ChasmTest.cs
Normal file
144
Content.IntegrationTests/Tests/Chasm/ChasmTest.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using Content.IntegrationTests.Tests.Movement;
|
||||
using Content.Shared.Chasm;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Weapons.Misc;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Chasm;
|
||||
|
||||
/// <summary>
|
||||
/// A test for chasms, which delete entities when a player walks over them.
|
||||
/// </summary>
|
||||
[TestOf(typeof(ChasmComponent))]
|
||||
public sealed class ChasmTest : MovementTest
|
||||
{
|
||||
private readonly EntProtoId _chasmProto = "FloorChasmEntity";
|
||||
private readonly EntProtoId _catWalkProto = "Catwalk";
|
||||
private readonly EntProtoId _grapplingGunProto = "WeaponGrapplingGun";
|
||||
|
||||
/// <summary>
|
||||
/// Test that a player falls into the chasm when walking over it.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChasmFallTest()
|
||||
{
|
||||
// Spawn a chasm.
|
||||
await SpawnTarget(_chasmProto);
|
||||
Assert.That(Delta(), Is.GreaterThan(0.5), "Player did not spawn left of the chasm.");
|
||||
|
||||
// Attempt (and fail) to walk past the chasm.
|
||||
// If you are modifying the default value of ChasmFallingComponent.DeletionTime this time might need to be adjusted.
|
||||
await Move(DirectionFlag.East, 0.5f);
|
||||
|
||||
// We should be falling right now.
|
||||
Assert.That(TryComp<ChasmFallingComponent>(Player, out var falling), "Player is not falling after walking over a chasm.");
|
||||
|
||||
var fallTime = (float)falling.DeletionTime.TotalSeconds;
|
||||
|
||||
// Wait until we get deleted.
|
||||
await Pair.RunSeconds(fallTime);
|
||||
|
||||
// Check that the player was deleted.
|
||||
AssertDeleted(Player);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that a catwalk placed over a chasm will protect a player from falling.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChasmCatwalkTest()
|
||||
{
|
||||
// Spawn a chasm.
|
||||
await SpawnTarget(_chasmProto);
|
||||
Assert.That(Delta(), Is.GreaterThan(0.5), "Player did not spawn left of the chasm.");
|
||||
|
||||
// Spawn a catwalk over the chasm.
|
||||
var catwalk = await Spawn(_catWalkProto);
|
||||
|
||||
// Attempt to walk past the chasm.
|
||||
await Move(DirectionFlag.East, 1f);
|
||||
|
||||
// We should be on the other side.
|
||||
Assert.That(Delta(), Is.LessThan(-0.5), "Player was unable to walk over a chasm with a catwalk.");
|
||||
|
||||
// Check that the player is not deleted.
|
||||
AssertExists(Player);
|
||||
|
||||
// Make sure the player is not falling right now.
|
||||
Assert.That(HasComp<ChasmFallingComponent>(Player), Is.False, "Player has ChasmFallingComponent after walking over a catwalk.");
|
||||
|
||||
// Delete the catwalk.
|
||||
await Delete(catwalk);
|
||||
|
||||
// Attempt (and fail) to walk past the chasm.
|
||||
await Move(DirectionFlag.West, 1f);
|
||||
|
||||
// Wait until we get deleted.
|
||||
await Pair.RunSeconds(5f);
|
||||
|
||||
// Check that the player was deleted
|
||||
AssertDeleted(Player);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that a player is able to cross a chasm by using a grappling gun.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChasmGrappleTest()
|
||||
{
|
||||
// Spawn a chasm.
|
||||
await SpawnTarget(_chasmProto);
|
||||
Assert.That(Delta(), Is.GreaterThan(0.5), "Player did not spawn left of the chasm.");
|
||||
|
||||
// Give the player a grappling gun.
|
||||
var grapplingGun = await PlaceInHands(_grapplingGunProto);
|
||||
await Pair.RunSeconds(2f); // guns have a cooldown when picking them up
|
||||
|
||||
// Shoot at the wall to the right.
|
||||
Assert.That(WallRight, Is.Not.Null, "No wall to shoot at!");
|
||||
await AttemptShoot(WallRight);
|
||||
await Pair.RunSeconds(2f);
|
||||
|
||||
// Check that the grappling hook is embedded into the wall.
|
||||
Assert.That(TryComp<GrapplingGunComponent>(grapplingGun, out var grapplingGunComp), "Grappling gun did not have GrapplingGunComponent.");
|
||||
Assert.That(grapplingGunComp.Projectile, Is.Not.Null, "Grappling gun projectile does not exist.");
|
||||
Assert.That(SEntMan.TryGetComponent<EmbeddableProjectileComponent>(grapplingGunComp.Projectile, out var embeddable), "Grappling hook was not embeddable.");
|
||||
Assert.That(embeddable.EmbeddedIntoUid, Is.EqualTo(ToServer(WallRight)), "Grappling hook was not embedded into the wall.");
|
||||
|
||||
// Check that the player is hooked.
|
||||
var grapplingSystem = SEntMan.System<SharedGrapplingGunSystem>();
|
||||
Assert.That(grapplingSystem.IsEntityHooked(SPlayer), "Player is not hooked to the wall.");
|
||||
Assert.That(HasComp<JointRelayTargetComponent>(Player), "Player does not have the JointRelayTargetComponent after using a grappling gun.");
|
||||
|
||||
// Attempt to walk past the chasm.
|
||||
await Move(DirectionFlag.East, 1f);
|
||||
|
||||
// We should be on the other side.
|
||||
Assert.That(Delta(), Is.LessThan(-0.5), "Player was unable to walk over a chasm with a grappling gun.");
|
||||
|
||||
// Check that the player is not deleted.
|
||||
AssertExists(Player);
|
||||
|
||||
// Make sure the player is not falling right now.
|
||||
Assert.That(HasComp<ChasmFallingComponent>(Player), Is.False, "Player has ChasmFallingComponent after moving over a chasm with a grappling gun.");
|
||||
|
||||
// Drop the grappling gun.
|
||||
await Drop();
|
||||
|
||||
// Check that the player no longer hooked.
|
||||
Assert.That(grapplingSystem.IsEntityHooked(SPlayer), Is.False, "Player still hooked after dropping the grappling gun.");
|
||||
Assert.That(HasComp<JointRelayTargetComponent>(Player), Is.False, "Player still has the JointRelayTargetComponent after dropping the grappling gun.");
|
||||
|
||||
// Attempt (and fail) to walk past the chasm.
|
||||
await Move(DirectionFlag.West, 1f);
|
||||
|
||||
// Wait until we get deleted.
|
||||
await Pair.RunSeconds(5f);
|
||||
|
||||
// Check that the player was deleted
|
||||
AssertDeleted(Player);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ using Content.Server.Construction.Components;
|
||||
using Content.Server.Gravity;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Item;
|
||||
@@ -85,7 +86,7 @@ public abstract partial class InteractionTest
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn an entity entity and set it as the target.
|
||||
/// Spawn an entity at the target coordinates and set it as the target.
|
||||
/// </summary>
|
||||
[MemberNotNull(nameof(Target), nameof(STarget), nameof(CTarget))]
|
||||
#pragma warning disable CS8774 // Member must have a non-null value when exiting.
|
||||
@@ -103,6 +104,22 @@ public abstract partial class InteractionTest
|
||||
}
|
||||
#pragma warning restore CS8774 // Member must have a non-null value when exiting.
|
||||
|
||||
/// <summary>
|
||||
/// Spawn an entity entity at the target coordinates without setting it as the target.
|
||||
/// </summary>
|
||||
protected async Task<NetEntity> Spawn(string prototype)
|
||||
{
|
||||
var entity = NetEntity.Invalid;
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
entity = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(TargetCoords)));
|
||||
});
|
||||
|
||||
await RunTicks(5);
|
||||
AssertPrototype(prototype, entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn an entity in preparation for deconstruction
|
||||
/// </summary>
|
||||
@@ -386,6 +403,119 @@ public abstract partial class InteractionTest
|
||||
|
||||
#endregion
|
||||
|
||||
# region Combat
|
||||
/// <summary>
|
||||
/// Returns if the player is currently in combat mode.
|
||||
/// </summary>
|
||||
protected bool IsInCombatMode()
|
||||
{
|
||||
if (!SEntMan.TryGetComponent(SPlayer, out CombatModeComponent? combat))
|
||||
{
|
||||
Assert.Fail($"Entity {SEntMan.ToPrettyString(SPlayer)} does not have a CombatModeComponent");
|
||||
return false;
|
||||
}
|
||||
|
||||
return combat.IsInCombatMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the combat mode for the player.
|
||||
/// </summary>
|
||||
protected async Task SetCombatMode(bool enabled)
|
||||
{
|
||||
if (!SEntMan.TryGetComponent(SPlayer, out CombatModeComponent? combat))
|
||||
{
|
||||
Assert.Fail($"Entity {SEntMan.ToPrettyString(SPlayer)} does not have a CombatModeComponent");
|
||||
return;
|
||||
}
|
||||
|
||||
await Server.WaitPost(() => SCombatMode.SetInCombatMode(SPlayer, enabled, combat));
|
||||
await RunTicks(1);
|
||||
|
||||
Assert.That(combat.IsInCombatMode, Is.EqualTo(enabled), $"Player could not set combate mode to {enabled}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the player shoot with their currently held gun.
|
||||
/// The player needs to be able to enter combat mode for this.
|
||||
/// This does not pass a target entity into the GunSystem, meaning that targets that
|
||||
/// need to be aimed at directly won't be hit.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Guns have a cooldown when picking them up.
|
||||
/// So make sure to wait a little after spawning a gun in the player's hand or this will fail.
|
||||
/// </remarks>
|
||||
/// <param name="target">The target coordinates to shoot at. Defaults to the current <see cref="TargetCoords"/>.</param>
|
||||
/// <param name="assert">If true this method will assert that the gun was successfully fired.</param>
|
||||
protected async Task AttemptShoot(NetCoordinates? target = null, bool assert = true)
|
||||
{
|
||||
var actualTarget = SEntMan.GetCoordinates(target ?? TargetCoords);
|
||||
|
||||
if (!SEntMan.TryGetComponent(SPlayer, out CombatModeComponent? combat))
|
||||
{
|
||||
Assert.Fail($"Entity {SEntMan.ToPrettyString(SPlayer)} does not have a CombatModeComponent");
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter combat mode before shooting.
|
||||
var wasInCombatMode = IsInCombatMode();
|
||||
await SetCombatMode(true);
|
||||
|
||||
Assert.That(SGun.TryGetGun(SPlayer, out var gunUid, out var gunComp), "Player was not holding a gun!");
|
||||
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
var success = SGun.AttemptShoot(SPlayer, gunUid, gunComp!, actualTarget);
|
||||
if (assert)
|
||||
Assert.That(success, "Gun failed to shoot.");
|
||||
});
|
||||
await RunTicks(1);
|
||||
|
||||
// If the player was not in combat mode before then disable it again.
|
||||
await SetCombatMode(wasInCombatMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the player shoot with their currently held gun.
|
||||
/// The player needs to be able to enter combat mode for this.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Guns have a cooldown when picking them up.
|
||||
/// So make sure to wait a little after spawning a gun in the player's hand or this will fail.
|
||||
/// </remarks>
|
||||
/// <param name="target">The target entity to shoot at. Defaults to the current <see cref="Target"/> entity.</param>
|
||||
/// <param name="assert">If true this method will assert that the gun was successfully fired.</param>
|
||||
protected async Task AttemptShoot(NetEntity? target = null, bool assert = true)
|
||||
{
|
||||
var actualTarget = target ?? Target;
|
||||
Assert.That(actualTarget, Is.Not.Null, "No target to shoot at!");
|
||||
|
||||
if (!SEntMan.TryGetComponent(SPlayer, out CombatModeComponent? combat))
|
||||
{
|
||||
Assert.Fail($"Entity {SEntMan.ToPrettyString(SPlayer)} does not have a CombatModeComponent");
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter combat mode before shooting.
|
||||
var wasInCombatMode = IsInCombatMode();
|
||||
await SetCombatMode(true);
|
||||
|
||||
Assert.That(SGun.TryGetGun(SPlayer, out var gunUid, out var gunComp), "Player was not holding a gun!");
|
||||
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
var success = SGun.AttemptShoot(SPlayer, gunUid, gunComp!, Position(actualTarget!.Value), ToServer(actualTarget));
|
||||
if (assert)
|
||||
Assert.That(success, "Gun failed to shoot.");
|
||||
});
|
||||
await RunTicks(1);
|
||||
|
||||
// If the player was not in combat mode before then disable it again.
|
||||
await SetCombatMode(wasInCombatMode);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Wait for any currently active DoAfters to finish.
|
||||
/// </summary>
|
||||
@@ -746,6 +876,18 @@ public abstract partial class InteractionTest
|
||||
return SEntMan.GetComponent<T>(ToServer(target!.Value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience method to check if the target has a component on the server.
|
||||
/// </summary>
|
||||
protected bool HasComp<T>(NetEntity? target = null) where T : IComponent
|
||||
{
|
||||
target ??= Target;
|
||||
if (target == null)
|
||||
Assert.Fail("No target specified");
|
||||
|
||||
return SEntMan.HasComponent<T>(ToServer(target));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Comp{T}"/>
|
||||
protected bool TryComp<T>(NetEntity? target, [NotNullWhen(true)] out T? comp) where T : IComponent
|
||||
{
|
||||
@@ -1013,7 +1155,7 @@ public abstract partial class InteractionTest
|
||||
}
|
||||
|
||||
Assert.That(control.GetType().IsAssignableTo(typeof(TControl)));
|
||||
return (TControl) control;
|
||||
return (TControl)control;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1177,8 +1319,8 @@ public abstract partial class InteractionTest
|
||||
{
|
||||
var atmosSystem = SEntMan.System<AtmosphereSystem>();
|
||||
var moles = new float[Atmospherics.AdjustedNumberOfGases];
|
||||
moles[(int) Gas.Oxygen] = 21.824779f;
|
||||
moles[(int) Gas.Nitrogen] = 82.10312f;
|
||||
moles[(int)Gas.Oxygen] = 21.824779f;
|
||||
moles[(int)Gas.Nitrogen] = 82.10312f;
|
||||
atmosSystem.SetMapAtmosphere(target, false, new GasMixture(moles, Atmospherics.T20C));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,12 +7,16 @@ using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Tools;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Players;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
@@ -21,8 +25,6 @@ using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.UnitTesting;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
using Robust.Client.State;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Interaction;
|
||||
|
||||
@@ -107,6 +109,8 @@ public abstract partial class InteractionTest
|
||||
protected SharedMapSystem MapSystem = default!;
|
||||
protected ISawmill SLogger = default!;
|
||||
protected SharedUserInterfaceSystem SUiSys = default!;
|
||||
protected SharedCombatModeSystem SCombatMode = default!;
|
||||
protected SharedGunSystem SGun = default!;
|
||||
|
||||
// CLIENT dependencies
|
||||
protected IEntityManager CEntMan = default!;
|
||||
@@ -124,7 +128,7 @@ public abstract partial class InteractionTest
|
||||
protected HandsComponent Hands = default!;
|
||||
protected DoAfterComponent DoAfters = default!;
|
||||
|
||||
public float TickPeriod => (float) STiming.TickPeriod.TotalSeconds;
|
||||
public float TickPeriod => (float)STiming.TickPeriod.TotalSeconds;
|
||||
|
||||
// Simple mob that has one hand and can perform misc interactions.
|
||||
[TestPrototypes]
|
||||
@@ -149,6 +153,7 @@ public abstract partial class InteractionTest
|
||||
tags:
|
||||
- CanPilot
|
||||
- type: UserInterface
|
||||
- type: CombatMode
|
||||
";
|
||||
|
||||
[SetUp]
|
||||
@@ -163,6 +168,7 @@ public abstract partial class InteractionTest
|
||||
ProtoMan = Server.ResolveDependency<IPrototypeManager>();
|
||||
Factory = Server.ResolveDependency<IComponentFactory>();
|
||||
STiming = Server.ResolveDependency<IGameTiming>();
|
||||
SLogger = Server.ResolveDependency<ILogManager>().RootSawmill;
|
||||
HandSys = SEntMan.System<HandsSystem>();
|
||||
InteractSys = SEntMan.System<SharedInteractionSystem>();
|
||||
ToolSys = SEntMan.System<ToolSystem>();
|
||||
@@ -173,20 +179,21 @@ public abstract partial class InteractionTest
|
||||
SConstruction = SEntMan.System<Server.Construction.ConstructionSystem>();
|
||||
STestSystem = SEntMan.System<InteractionTestSystem>();
|
||||
Stack = SEntMan.System<StackSystem>();
|
||||
SLogger = Server.ResolveDependency<ILogManager>().RootSawmill;
|
||||
SUiSys = Client.System<SharedUserInterfaceSystem>();
|
||||
SUiSys = SEntMan.System<SharedUserInterfaceSystem>();
|
||||
SCombatMode = SEntMan.System<SharedCombatModeSystem>();
|
||||
SGun = SEntMan.System<SharedGunSystem>();
|
||||
|
||||
// client dependencies
|
||||
CEntMan = Client.ResolveDependency<IEntityManager>();
|
||||
UiMan = Client.ResolveDependency<IUserInterfaceManager>();
|
||||
CTiming = Client.ResolveDependency<IGameTiming>();
|
||||
InputManager = Client.ResolveDependency<IInputManager>();
|
||||
CLogger = Client.ResolveDependency<ILogManager>().RootSawmill;
|
||||
InputSystem = CEntMan.System<Robust.Client.GameObjects.InputSystem>();
|
||||
CTestSystem = CEntMan.System<InteractionTestSystem>();
|
||||
CConSys = CEntMan.System<ConstructionSystem>();
|
||||
ExamineSys = CEntMan.System<ExamineSystem>();
|
||||
CLogger = Client.ResolveDependency<ILogManager>().RootSawmill;
|
||||
CUiSys = Client.System<SharedUserInterfaceSystem>();
|
||||
CUiSys = CEntMan.System<SharedUserInterfaceSystem>();
|
||||
|
||||
// Setup map.
|
||||
await Pair.CreateTestMap();
|
||||
|
||||
@@ -24,6 +24,15 @@ public abstract class MovementTest : InteractionTest
|
||||
/// </summary>
|
||||
protected virtual bool AddWalls => true;
|
||||
|
||||
/// <summary>
|
||||
/// The wall entity on the left side.
|
||||
/// </summary>
|
||||
protected NetEntity? WallLeft;
|
||||
/// <summary>
|
||||
/// The wall entity on the right side.
|
||||
/// </summary>
|
||||
protected NetEntity? WallRight;
|
||||
|
||||
[SetUp]
|
||||
public override async Task Setup()
|
||||
{
|
||||
@@ -38,8 +47,11 @@ public abstract class MovementTest : InteractionTest
|
||||
|
||||
if (AddWalls)
|
||||
{
|
||||
await SpawnEntity("WallSolid", pCoords.Offset(new Vector2(-Tiles, 0)));
|
||||
await SpawnEntity("WallSolid", pCoords.Offset(new Vector2(Tiles, 0)));
|
||||
var sWallLeft = await SpawnEntity("WallSolid", pCoords.Offset(new Vector2(-Tiles, 0)));
|
||||
var sWallRight = await SpawnEntity("WallSolid", pCoords.Offset(new Vector2(Tiles, 0)));
|
||||
|
||||
WallLeft = SEntMan.GetNetEntity(sWallLeft);
|
||||
WallRight = SEntMan.GetNetEntity(sWallRight);
|
||||
}
|
||||
|
||||
await AddGravity();
|
||||
|
||||
@@ -204,38 +204,40 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Attempts to shoot at the target coordinates. Resets the shot counter after every shot.
|
||||
/// </summary>
|
||||
public void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, EntityCoordinates toCoordinates, EntityUid? target = null)
|
||||
public bool AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, EntityCoordinates toCoordinates, EntityUid? target = null)
|
||||
{
|
||||
gun.ShootCoordinates = toCoordinates;
|
||||
AttemptShoot(user, gunUid, gun);
|
||||
gun.ShotCounter = 0;
|
||||
gun.Target = target;
|
||||
var result = AttemptShoot(user, gunUid, gun);
|
||||
gun.ShotCounter = 0;
|
||||
DirtyField(gunUid, gun, nameof(GunComponent.ShotCounter));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shoots by assuming the gun is the user at default coordinates.
|
||||
/// </summary>
|
||||
public void AttemptShoot(EntityUid gunUid, GunComponent gun)
|
||||
public bool AttemptShoot(EntityUid gunUid, GunComponent gun)
|
||||
{
|
||||
var coordinates = new EntityCoordinates(gunUid, gun.DefaultDirection);
|
||||
gun.ShootCoordinates = coordinates;
|
||||
AttemptShoot(gunUid, gunUid, gun);
|
||||
var result = AttemptShoot(gunUid, gunUid, gun);
|
||||
gun.ShotCounter = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun)
|
||||
private bool AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun)
|
||||
{
|
||||
if (gun.FireRateModified <= 0f ||
|
||||
!_actionBlockerSystem.CanAttack(user))
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
var toCoordinates = gun.ShootCoordinates;
|
||||
|
||||
if (toCoordinates == null)
|
||||
return;
|
||||
return false;
|
||||
|
||||
var curTime = Timing.CurTime;
|
||||
|
||||
@@ -247,16 +249,16 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
};
|
||||
RaiseLocalEvent(gunUid, ref prevention);
|
||||
if (prevention.Cancelled)
|
||||
return;
|
||||
return false;
|
||||
|
||||
RaiseLocalEvent(user, ref prevention);
|
||||
if (prevention.Cancelled)
|
||||
return;
|
||||
return false;
|
||||
|
||||
// Need to do this to play the clicking sound for empty automatic weapons
|
||||
// but not play anything for burst fire.
|
||||
if (gun.NextFire > curTime)
|
||||
return;
|
||||
return false;
|
||||
|
||||
var fireRate = TimeSpan.FromSeconds(1f / gun.FireRateModified);
|
||||
|
||||
@@ -315,7 +317,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
gun.BurstActivated = false;
|
||||
gun.BurstShotsCount = 0;
|
||||
gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
var fromCoordinates = Transform(user).Coordinates;
|
||||
@@ -355,10 +357,10 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
// May cause prediction issues? Needs more tweaking
|
||||
gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds));
|
||||
Audio.PlayPredicted(gun.SoundEmpty, gunUid, user);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle burstfire
|
||||
@@ -383,13 +385,14 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
RaiseLocalEvent(gunUid, ref shotEv);
|
||||
|
||||
if (!userImpulse || !TryComp<PhysicsComponent>(user, out var userPhysics))
|
||||
return;
|
||||
return true;
|
||||
|
||||
var shooterEv = new ShooterImpulseEvent();
|
||||
RaiseLocalEvent(user, ref shooterEv);
|
||||
|
||||
if (shooterEv.Push)
|
||||
CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Shoot(
|
||||
|
||||
Reference in New Issue
Block a user