Adds force-gun (#16561)
This commit is contained in:
56
Content.Shared/Weapons/Misc/BaseForceGunComponent.cs
Normal file
56
Content.Shared/Weapons/Misc/BaseForceGunComponent.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Shared.Weapons.Misc;
|
||||
|
||||
public abstract class BaseForceGunComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("lineColor"), AutoNetworkedField]
|
||||
public Color LineColor = Color.Orange;
|
||||
|
||||
/// <summary>
|
||||
/// The entity the tethered target has a joint to.
|
||||
/// </summary>
|
||||
[DataField("tetherEntity"), AutoNetworkedField]
|
||||
public EntityUid? TetherEntity;
|
||||
|
||||
/// <summary>
|
||||
/// The entity currently tethered.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("tethered"), AutoNetworkedField]
|
||||
public virtual EntityUid? Tethered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Can the tethergun unanchor entities.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("canUnanchor"), AutoNetworkedField]
|
||||
public bool CanUnanchor = false;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("canTetherAlive"), AutoNetworkedField]
|
||||
public bool CanTetherAlive = false;
|
||||
|
||||
/// <summary>
|
||||
/// Max force between the tether entity and the tethered target.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("maxForce"), AutoNetworkedField]
|
||||
public float MaxForce = 200f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("frequency"), AutoNetworkedField]
|
||||
public float Frequency = 10f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("dampingRatio"), AutoNetworkedField]
|
||||
public float DampingRatio = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum amount of mass a tethered entity can have.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("massLimit"), AutoNetworkedField]
|
||||
public float MassLimit = 100f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("sound"), AutoNetworkedField]
|
||||
public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Weapons/weoweo.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithLoop(true).WithVolume(-8f),
|
||||
};
|
||||
|
||||
public IPlayingAudioStream? Stream;
|
||||
}
|
||||
29
Content.Shared/Weapons/Misc/ForceGunComponent.cs
Normal file
29
Content.Shared/Weapons/Misc/ForceGunComponent.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Weapons.Misc;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||
public sealed partial class ForceGunComponent : BaseForceGunComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum distance to throw entities.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("throwDistance"), AutoNetworkedField]
|
||||
public float ThrowDistance = 15f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("throwForce"), AutoNetworkedField]
|
||||
public float ThrowForce = 30f;
|
||||
|
||||
/// <summary>
|
||||
/// The entity currently tethered.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("tethered"), AutoNetworkedField]
|
||||
public override EntityUid? Tethered { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("soundLaunch")]
|
||||
public SoundSpecifier? LaunchSound = new SoundPathSpecifier("/Audio/Weapons/soup.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVolume(5f),
|
||||
};
|
||||
}
|
||||
54
Content.Shared/Weapons/Misc/SharedTetherGunSystem.Force.cs
Normal file
54
Content.Shared/Weapons/Misc/SharedTetherGunSystem.Force.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Shared.Weapons.Misc;
|
||||
|
||||
public abstract partial class SharedTetherGunSystem
|
||||
{
|
||||
private void InitializeForce()
|
||||
{
|
||||
SubscribeLocalEvent<ForceGunComponent, AfterInteractEvent>(OnForceRanged);
|
||||
SubscribeLocalEvent<ForceGunComponent, ActivateInWorldEvent>(OnForceActivate);
|
||||
}
|
||||
|
||||
private void OnForceActivate(EntityUid uid, ForceGunComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
StopTether(uid, component);
|
||||
}
|
||||
|
||||
private void OnForceRanged(EntityUid uid, ForceGunComponent component, AfterInteractEvent args)
|
||||
{
|
||||
if (IsTethered(component))
|
||||
{
|
||||
if (!args.ClickLocation.TryDistance(EntityManager, TransformSystem, Transform(uid).Coordinates,
|
||||
out var distance) ||
|
||||
distance > component.ThrowDistance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// URGH, soon
|
||||
// Need auto states to be nicer + powercelldraw to be nicer
|
||||
if (!_netManager.IsServer)
|
||||
return;
|
||||
|
||||
// Launch
|
||||
var tethered = component.Tethered;
|
||||
StopTether(uid, component, land: false);
|
||||
_throwing.TryThrow(tethered!.Value, args.ClickLocation, component.ThrowForce, playSound: false);
|
||||
|
||||
_audio.PlayPredicted(component.LaunchSound, uid, null);
|
||||
}
|
||||
else if (args.Target != null)
|
||||
{
|
||||
// Pickup
|
||||
if (TryTether(uid, args.Target.Value, args.User, component))
|
||||
TransformSystem.SetCoordinates(component.TetherEntity!.Value, new EntityCoordinates(uid, new Vector2(0.0f, -0.8f)));
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsTethered(ForceGunComponent component)
|
||||
{
|
||||
return component.Tethered != null;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Toggleable;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -16,16 +17,18 @@ using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Weapons.Misc;
|
||||
|
||||
public abstract class SharedTetherGunSystem : EntitySystem
|
||||
public abstract partial class SharedTetherGunSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
[Dependency] private readonly MobStateSystem _mob = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedJointSystem _joints = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwing = default!;
|
||||
[Dependency] private readonly ThrownItemSystem _thrown = default!;
|
||||
|
||||
private const string TetherJoint = "tether";
|
||||
@@ -42,6 +45,8 @@ public abstract class SharedTetherGunSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<TetheredComponent, BuckleAttemptEvent>(OnTetheredBuckleAttempt);
|
||||
SubscribeLocalEvent<TetheredComponent, UpdateCanMoveEvent>(OnTetheredUpdateCanMove);
|
||||
|
||||
InitializeForce();
|
||||
}
|
||||
|
||||
private void OnTetheredBuckleAttempt(EntityUid uid, TetheredComponent component, ref BuckleAttemptEvent args)
|
||||
@@ -115,7 +120,8 @@ public abstract class SharedTetherGunSystem : EntitySystem
|
||||
gun = null;
|
||||
|
||||
if (!TryComp<HandsComponent>(user, out var hands) ||
|
||||
!TryComp(hands.ActiveHandEntity, out gun))
|
||||
!TryComp(hands.ActiveHandEntity, out gun) ||
|
||||
_container.IsEntityInContainer(user))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -129,18 +135,19 @@ public abstract class SharedTetherGunSystem : EntitySystem
|
||||
StopTether(uid, component);
|
||||
}
|
||||
|
||||
public void TryTether(EntityUid gun, EntityUid target, EntityUid? user, TetherGunComponent? component = null)
|
||||
public bool TryTether(EntityUid gun, EntityUid target, EntityUid? user, BaseForceGunComponent? component = null)
|
||||
{
|
||||
if (!Resolve(gun, ref component))
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (!CanTether(gun, component, target, user))
|
||||
return;
|
||||
return false;
|
||||
|
||||
StartTether(gun, component, target, user);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual bool CanTether(EntityUid uid, TetherGunComponent component, EntityUid target, EntityUid? user)
|
||||
protected virtual bool CanTether(EntityUid uid, BaseForceGunComponent component, EntityUid target, EntityUid? user)
|
||||
{
|
||||
if (HasComp<TetheredComponent>(target) || !TryComp<PhysicsComponent>(target, out var physics))
|
||||
return false;
|
||||
@@ -160,7 +167,7 @@ public abstract class SharedTetherGunSystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void StartTether(EntityUid gunUid, TetherGunComponent component, EntityUid target, EntityUid? user,
|
||||
protected virtual void StartTether(EntityUid gunUid, BaseForceGunComponent component, EntityUid target, EntityUid? user,
|
||||
PhysicsComponent? targetPhysics = null, TransformComponent? targetXform = null)
|
||||
{
|
||||
if (!Resolve(target, ref targetPhysics, ref targetXform))
|
||||
@@ -212,7 +219,7 @@ public abstract class SharedTetherGunSystem : EntitySystem
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
protected virtual void StopTether(EntityUid gunUid, TetherGunComponent component, bool transfer = false)
|
||||
protected virtual void StopTether(EntityUid gunUid, BaseForceGunComponent component, bool land = true, bool transfer = false)
|
||||
{
|
||||
if (component.Tethered == null)
|
||||
return;
|
||||
@@ -229,8 +236,11 @@ public abstract class SharedTetherGunSystem : EntitySystem
|
||||
|
||||
if (TryComp<PhysicsComponent>(component.Tethered, out var targetPhysics))
|
||||
{
|
||||
var thrown = EnsureComp<ThrownItemComponent>(component.Tethered.Value);
|
||||
_thrown.LandComponent(component.Tethered.Value, thrown, targetPhysics);
|
||||
if (land)
|
||||
{
|
||||
var thrown = EnsureComp<ThrownItemComponent>(component.Tethered.Value);
|
||||
_thrown.LandComponent(component.Tethered.Value, thrown, targetPhysics, true);
|
||||
}
|
||||
|
||||
_physics.SetBodyStatus(targetPhysics, BodyStatus.OnGround);
|
||||
_physics.SetSleepingAllowed(component.Tethered.Value, targetPhysics, true);
|
||||
|
||||
@@ -1,58 +1,16 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Weapons.Misc;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class TetherGunComponent : Component
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||
public sealed partial class TetherGunComponent : BaseForceGunComponent
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("maxDistance"), AutoNetworkedField]
|
||||
public float MaxDistance = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// The entity the tethered target has a joint to.
|
||||
/// </summary>
|
||||
[DataField("tetherEntity"), AutoNetworkedField]
|
||||
public EntityUid? TetherEntity;
|
||||
|
||||
/// <summary>
|
||||
/// The entity currently tethered.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("tethered"), AutoNetworkedField]
|
||||
public EntityUid? Tethered;
|
||||
|
||||
/// <summary>
|
||||
/// Can the tethergun unanchor entities.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("canUnanchor"), AutoNetworkedField]
|
||||
public bool CanUnanchor = false;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("canTetherAlive"), AutoNetworkedField]
|
||||
public bool CanTetherAlive = false;
|
||||
|
||||
/// <summary>
|
||||
/// Max force between the tether entity and the tethered target.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("maxForce"), AutoNetworkedField]
|
||||
public float MaxForce = 200f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("frequency"), AutoNetworkedField]
|
||||
public float Frequency = 10f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("dampingRatio"), AutoNetworkedField]
|
||||
public float DampingRatio = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum amount of mass a tethered entity can have.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("massLimit"), AutoNetworkedField]
|
||||
public float MassLimit = 100f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("sound"), AutoNetworkedField]
|
||||
public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Weapons/weoweo.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithLoop(true).WithVolume(-8f),
|
||||
};
|
||||
|
||||
public IPlayingAudioStream? Stream;
|
||||
public override EntityUid? Tethered { get; set; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user