Adds force-gun (#16561)

This commit is contained in:
metalgearsloth
2023-05-19 17:10:31 +10:00
committed by GitHub
parent eb94a785f9
commit 4efb41aa58
26 changed files with 373 additions and 76 deletions

View File

@@ -19,6 +19,8 @@ public sealed class TetherGunOverlay : Overlay
{ {
var query = _entManager.EntityQueryEnumerator<TetheredComponent>(); var query = _entManager.EntityQueryEnumerator<TetheredComponent>();
var xformQuery = _entManager.GetEntityQuery<TransformComponent>(); var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
var tetherQuery = _entManager.GetEntityQuery<TetherGunComponent>();
var forceQuery = _entManager.GetEntityQuery<ForceGunComponent>();
var worldHandle = args.WorldHandle; var worldHandle = args.WorldHandle;
var xformSystem = _entManager.System<SharedTransformSystem>(); var xformSystem = _entManager.System<SharedTransformSystem>();
@@ -46,7 +48,18 @@ public sealed class TetherGunOverlay : Overlay
var box = new Box2(-Width, -length, Width, length); var box = new Box2(-Width, -length, Width, length);
var rotated = new Box2Rotated(box.Translated(midPoint), angle, midPoint); var rotated = new Box2Rotated(box.Translated(midPoint), angle, midPoint);
worldHandle.DrawRect(rotated, Color.Orange.WithAlpha(0.3f)); var color = Color.Red;
if (forceQuery.TryGetComponent(tethered.Tetherer, out var force))
{
color = force.LineColor;
}
else if (tetherQuery.TryGetComponent(tethered.Tetherer, out var tether))
{
color = tether.LineColor;
}
worldHandle.DrawRect(rotated, color.WithAlpha(0.3f));
} }
} }
} }

View File

@@ -22,16 +22,26 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<TetheredComponent, ComponentStartup>(OnTetheredStartup); SubscribeLocalEvent<TetheredComponent, ComponentStartup>(OnTetheredStartup);
SubscribeLocalEvent<TetheredComponent, ComponentShutdown>(OnTetheredShutdown); SubscribeLocalEvent<TetheredComponent, ComponentShutdown>(OnTetheredShutdown);
SubscribeLocalEvent<TetherGunComponent, AfterAutoHandleStateEvent>(OnAfterState);
SubscribeLocalEvent<ForceGunComponent, AfterAutoHandleStateEvent>(OnAfterState);
_overlay.AddOverlay(new TetherGunOverlay(EntityManager)); _overlay.AddOverlay(new TetherGunOverlay(EntityManager));
} }
private void OnAfterState(EntityUid uid, BaseForceGunComponent component, ref AfterAutoHandleStateEvent args)
{
if (!TryComp<SpriteComponent>(component.Tethered, out var sprite))
return;
sprite.Color = component.LineColor;
}
public override void Shutdown() public override void Shutdown()
{ {
base.Shutdown(); base.Shutdown();
_overlay.RemoveOverlay<TetherGunOverlay>(); _overlay.RemoveOverlay<TetherGunOverlay>();
} }
protected override bool CanTether(EntityUid uid, TetherGunComponent component, EntityUid target, EntityUid? user) protected override bool CanTether(EntityUid uid, BaseForceGunComponent component, EntityUid target, EntityUid? user)
{ {
// Need powercells predicted sadly :< // Need powercells predicted sadly :<
return false; return false;
@@ -88,9 +98,18 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
private void OnTetheredStartup(EntityUid uid, TetheredComponent component, ComponentStartup args) private void OnTetheredStartup(EntityUid uid, TetheredComponent component, ComponentStartup args)
{ {
if (!TryComp<SpriteComponent>(uid, out var sprite)) if (!TryComp<SpriteComponent>(uid, out var sprite))
{
return; return;
}
sprite.Color = Color.Orange; if (TryComp<ForceGunComponent>(component.Tetherer, out var force))
{
sprite.Color = force.LineColor;
}
else if (TryComp<TetherGunComponent>(component.Tetherer, out var tether))
{
sprite.Color = tether.LineColor;
}
} }
private void OnTetheredShutdown(EntityUid uid, TetheredComponent component, ComponentShutdown args) private void OnTetheredShutdown(EntityUid uid, TetheredComponent component, ComponentShutdown args)

View File

@@ -1,5 +1,4 @@
using Content.Server.PowerCell; using Content.Server.PowerCell;
using Content.Shared.PowerCell.Components;
using Content.Shared.Weapons.Misc; using Content.Shared.Weapons.Misc;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
@@ -13,14 +12,15 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<TetherGunComponent, PowerCellSlotEmptyEvent>(OnGunEmpty); SubscribeLocalEvent<TetherGunComponent, PowerCellSlotEmptyEvent>(OnGunEmpty);
SubscribeLocalEvent<ForceGunComponent, PowerCellSlotEmptyEvent>(OnGunEmpty);
} }
private void OnGunEmpty(EntityUid uid, TetherGunComponent component, ref PowerCellSlotEmptyEvent args) private void OnGunEmpty(EntityUid uid, BaseForceGunComponent component, ref PowerCellSlotEmptyEvent args)
{ {
StopTether(uid, component); StopTether(uid, component);
} }
protected override bool CanTether(EntityUid uid, TetherGunComponent component, EntityUid target, EntityUid? user) protected override bool CanTether(EntityUid uid, BaseForceGunComponent component, EntityUid target, EntityUid? user)
{ {
if (!base.CanTether(uid, component, target, user)) if (!base.CanTether(uid, component, target, user))
return false; return false;
@@ -31,16 +31,16 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
return true; return true;
} }
protected override void StartTether(EntityUid gunUid, TetherGunComponent component, EntityUid target, EntityUid? user, protected override void StartTether(EntityUid gunUid, BaseForceGunComponent component, EntityUid target, EntityUid? user,
PhysicsComponent? targetPhysics = null, TransformComponent? targetXform = null) PhysicsComponent? targetPhysics = null, TransformComponent? targetXform = null)
{ {
base.StartTether(gunUid, component, target, user, targetPhysics, targetXform); base.StartTether(gunUid, component, target, user, targetPhysics, targetXform);
_cell.SetPowerCellDrawEnabled(gunUid, true); _cell.SetPowerCellDrawEnabled(gunUid, true);
} }
protected override void StopTether(EntityUid gunUid, TetherGunComponent component, bool transfer = false) protected override void StopTether(EntityUid gunUid, BaseForceGunComponent component, bool land = true, bool transfer = false)
{ {
base.StopTether(gunUid, component, transfer); base.StopTether(gunUid, component, land, transfer);
_cell.SetPowerCellDrawEnabled(gunUid, false); _cell.SetPowerCellDrawEnabled(gunUid, false);
} }
} }

View File

@@ -51,9 +51,12 @@ public abstract class SharedEmitSoundSystem : EntitySystem
private void OnEmitSoundOnLand(EntityUid uid, BaseEmitSoundComponent component, ref LandEvent args) private void OnEmitSoundOnLand(EntityUid uid, BaseEmitSoundComponent component, ref LandEvent args)
{ {
if (!TryComp<TransformComponent>(uid, out var xform) || if (!args.PlaySound ||
!TryComp<TransformComponent>(uid, out var xform) ||
!_mapManager.TryGetGrid(xform.GridUid, out var grid)) !_mapManager.TryGetGrid(xform.GridUid, out var grid))
{
return; return;
}
var tile = grid.GetTileRef(xform.Coordinates); var tile = grid.GetTileRef(xform.Coordinates);

View File

@@ -6,7 +6,7 @@ namespace Content.Shared.Throwing
/// Raised when an entity that was thrown lands. This occurs before they stop moving and is when their tile-friction is reapplied. /// Raised when an entity that was thrown lands. This occurs before they stop moving and is when their tile-friction is reapplied.
/// </summary> /// </summary>
[ByRefEvent] [ByRefEvent]
public readonly record struct LandEvent(EntityUid? User); public readonly record struct LandEvent(EntityUid? User, bool PlaySound);
/// <summary> /// <summary>
/// Raised when a thrown entity is no longer moving. /// Raised when a thrown entity is no longer moving.

View File

@@ -3,6 +3,7 @@ using Content.Shared.Interaction;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Content.Shared.Projectiles; using Content.Shared.Projectiles;
using Content.Shared.Tag; using Content.Shared.Tag;
using Robust.Shared.Map;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
@@ -25,9 +26,27 @@ public sealed class ThrowingSystem : EntitySystem
[Dependency] private readonly SharedGravitySystem _gravity = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly ThrownItemSystem _thrownSystem = default!; [Dependency] private readonly ThrownItemSystem _thrownSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly TagSystem _tagSystem = default!;
public void TryThrow(
EntityUid uid,
EntityCoordinates coordinates,
float strength = 1.0f,
EntityUid? user = null,
float pushbackRatio = PushbackDefault,
bool playSound = true)
{
var thrownPos = Transform(uid).MapPosition;
var mapPos = coordinates.ToMap(EntityManager, _transform);
if (mapPos.MapId != thrownPos.MapId)
return;
TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, playSound);
}
/// <summary> /// <summary>
/// Tries to throw the entity if it has a physics component, otherwise does nothing. /// Tries to throw the entity if it has a physics component, otherwise does nothing.
/// </summary> /// </summary>
@@ -39,7 +58,8 @@ public sealed class ThrowingSystem : EntitySystem
Vector2 direction, Vector2 direction,
float strength = 1.0f, float strength = 1.0f,
EntityUid? user = null, EntityUid? user = null,
float pushbackRatio = PushbackDefault) float pushbackRatio = PushbackDefault,
bool playSound = true)
{ {
var physicsQuery = GetEntityQuery<PhysicsComponent>(); var physicsQuery = GetEntityQuery<PhysicsComponent>();
if (!physicsQuery.TryGetComponent(uid, out var physics)) if (!physicsQuery.TryGetComponent(uid, out var physics))
@@ -57,7 +77,8 @@ public sealed class ThrowingSystem : EntitySystem
tagQuery, tagQuery,
strength, strength,
user, user,
pushbackRatio); pushbackRatio,
playSound);
} }
/// <summary> /// <summary>
@@ -75,7 +96,8 @@ public sealed class ThrowingSystem : EntitySystem
EntityQuery<TagComponent> tagQuery, EntityQuery<TagComponent> tagQuery,
float strength = 1.0f, float strength = 1.0f,
EntityUid? user = null, EntityUid? user = null,
float pushbackRatio = PushbackDefault) float pushbackRatio = PushbackDefault,
bool playSound = true)
{ {
if (strength <= 0 || direction == Vector2.Infinity || direction == Vector2.NaN || direction == Vector2.Zero) if (strength <= 0 || direction == Vector2.Infinity || direction == Vector2.NaN || direction == Vector2.Zero)
return; return;
@@ -105,11 +127,11 @@ public sealed class ThrowingSystem : EntitySystem
_physics.ApplyLinearImpulse(uid, impulseVector, body: physics); _physics.ApplyLinearImpulse(uid, impulseVector, body: physics);
// Estimate time to arrival so we can apply OnGround status and slow it much faster. // Estimate time to arrival so we can apply OnGround status and slow it much faster.
var time = (direction / strength).Length; var time = direction.Length / strength;
if (time < FlyTime) if (time < FlyTime)
{ {
_thrownSystem.LandComponent(uid, comp, physics); _thrownSystem.LandComponent(uid, comp, physics, playSound);
} }
else else
{ {
@@ -120,7 +142,7 @@ public sealed class ThrowingSystem : EntitySystem
if (physics.Deleted) if (physics.Deleted)
return; return;
_thrownSystem.LandComponent(uid, comp, physics); _thrownSystem.LandComponent(uid, comp, physics, playSound);
}); });
} }

View File

@@ -116,7 +116,7 @@ namespace Content.Shared.Throwing
EntityManager.RemoveComponent<ThrownItemComponent>(uid); EntityManager.RemoveComponent<ThrownItemComponent>(uid);
} }
public void LandComponent(EntityUid uid, ThrownItemComponent thrownItem, PhysicsComponent physics) public void LandComponent(EntityUid uid, ThrownItemComponent thrownItem, PhysicsComponent physics, bool playSound)
{ {
_physics.SetBodyStatus(physics, BodyStatus.OnGround); _physics.SetBodyStatus(physics, BodyStatus.OnGround);
@@ -138,7 +138,7 @@ namespace Content.Shared.Throwing
_adminLogger.Add(LogType.Landed, LogImpact.Low, $"{ToPrettyString(landing):entity} thrown by {ToPrettyString(thrownItem.Thrower.Value):thrower} landed."); _adminLogger.Add(LogType.Landed, LogImpact.Low, $"{ToPrettyString(landing):entity} thrown by {ToPrettyString(thrownItem.Thrower.Value):thrower} landed.");
_broadphase.RegenerateContacts(physics); _broadphase.RegenerateContacts(physics);
var landEvent = new LandEvent(thrownItem.Thrower); var landEvent = new LandEvent(thrownItem.Thrower, playSound);
RaiseLocalEvent(landing, ref landEvent); RaiseLocalEvent(landing, ref landEvent);
} }

View 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;
}

View 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),
};
}

View 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;
}
}

View File

@@ -7,6 +7,7 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
using Content.Shared.Throwing; using Content.Shared.Throwing;
using Content.Shared.Toggleable; using Content.Shared.Toggleable;
using Robust.Shared.Containers;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Physics; using Robust.Shared.Physics;
@@ -16,16 +17,18 @@ using Robust.Shared.Serialization;
namespace Content.Shared.Weapons.Misc; 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 INetManager _netManager = default!;
[Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly ActionBlockerSystem _blocker = default!;
[Dependency] private readonly MobStateSystem _mob = 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 SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedJointSystem _joints = default!; [Dependency] private readonly SharedJointSystem _joints = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!; [Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
[Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly ThrownItemSystem _thrown = default!; [Dependency] private readonly ThrownItemSystem _thrown = default!;
private const string TetherJoint = "tether"; private const string TetherJoint = "tether";
@@ -42,6 +45,8 @@ public abstract class SharedTetherGunSystem : EntitySystem
SubscribeLocalEvent<TetheredComponent, BuckleAttemptEvent>(OnTetheredBuckleAttempt); SubscribeLocalEvent<TetheredComponent, BuckleAttemptEvent>(OnTetheredBuckleAttempt);
SubscribeLocalEvent<TetheredComponent, UpdateCanMoveEvent>(OnTetheredUpdateCanMove); SubscribeLocalEvent<TetheredComponent, UpdateCanMoveEvent>(OnTetheredUpdateCanMove);
InitializeForce();
} }
private void OnTetheredBuckleAttempt(EntityUid uid, TetheredComponent component, ref BuckleAttemptEvent args) private void OnTetheredBuckleAttempt(EntityUid uid, TetheredComponent component, ref BuckleAttemptEvent args)
@@ -115,7 +120,8 @@ public abstract class SharedTetherGunSystem : EntitySystem
gun = null; gun = null;
if (!TryComp<HandsComponent>(user, out var hands) || if (!TryComp<HandsComponent>(user, out var hands) ||
!TryComp(hands.ActiveHandEntity, out gun)) !TryComp(hands.ActiveHandEntity, out gun) ||
_container.IsEntityInContainer(user))
{ {
return false; return false;
} }
@@ -129,18 +135,19 @@ public abstract class SharedTetherGunSystem : EntitySystem
StopTether(uid, component); 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)) if (!Resolve(gun, ref component))
return; return false;
if (!CanTether(gun, component, target, user)) if (!CanTether(gun, component, target, user))
return; return false;
StartTether(gun, component, target, user); 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)) if (HasComp<TetheredComponent>(target) || !TryComp<PhysicsComponent>(target, out var physics))
return false; return false;
@@ -160,7 +167,7 @@ public abstract class SharedTetherGunSystem : EntitySystem
return true; 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) PhysicsComponent? targetPhysics = null, TransformComponent? targetXform = null)
{ {
if (!Resolve(target, ref targetPhysics, ref targetXform)) if (!Resolve(target, ref targetPhysics, ref targetXform))
@@ -212,7 +219,7 @@ public abstract class SharedTetherGunSystem : EntitySystem
Dirty(component); 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) if (component.Tethered == null)
return; return;
@@ -228,9 +235,12 @@ public abstract class SharedTetherGunSystem : EntitySystem
} }
if (TryComp<PhysicsComponent>(component.Tethered, out var targetPhysics)) if (TryComp<PhysicsComponent>(component.Tethered, out var targetPhysics))
{
if (land)
{ {
var thrown = EnsureComp<ThrownItemComponent>(component.Tethered.Value); var thrown = EnsureComp<ThrownItemComponent>(component.Tethered.Value);
_thrown.LandComponent(component.Tethered.Value, thrown, targetPhysics); _thrown.LandComponent(component.Tethered.Value, thrown, targetPhysics, true);
}
_physics.SetBodyStatus(targetPhysics, BodyStatus.OnGround); _physics.SetBodyStatus(targetPhysics, BodyStatus.OnGround);
_physics.SetSleepingAllowed(component.Tethered.Value, targetPhysics, true); _physics.SetSleepingAllowed(component.Tethered.Value, targetPhysics, true);

View File

@@ -1,58 +1,16 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Shared.Weapons.Misc; namespace Content.Shared.Weapons.Misc;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
public sealed partial class TetherGunComponent : Component public sealed partial class TetherGunComponent : BaseForceGunComponent
{ {
[ViewVariables(VVAccess.ReadWrite), DataField("maxDistance"), AutoNetworkedField] [ViewVariables(VVAccess.ReadWrite), DataField("maxDistance"), AutoNetworkedField]
public float MaxDistance = 10f; public float MaxDistance = 10f;
/// <summary>
/// The entity the tethered target has a joint to.
/// </summary>
[DataField("tetherEntity"), AutoNetworkedField]
public EntityUid? TetherEntity;
/// <summary> /// <summary>
/// The entity currently tethered. /// The entity currently tethered.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("tethered"), AutoNetworkedField] [ViewVariables(VVAccess.ReadWrite), DataField("tethered"), AutoNetworkedField]
public EntityUid? Tethered; public override 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;
} }

View File

@@ -17,3 +17,7 @@ pierce.ogg taken from: https://github.com/tgstation/tgstation/commit/106cd26fc00
- files: ["weoweo.ogg"] - files: ["weoweo.ogg"]
license: "SONNISS #GAMEAUDIOGDC BUNDLE LICENSING" license: "SONNISS #GAMEAUDIOGDC BUNDLE LICENSING"
copyright: "Taken from Sonniss.com - GDC 2023 - Systematic Sound - TonalElements Obscurum - Dark Drones" copyright: "Taken from Sonniss.com - GDC 2023 - Systematic Sound - TonalElements Obscurum - Dark Drones"
- files: ["soup.ogg"]
license: "SONNISS #GAMEAUDIOGDC BUNDLE LICENSING"
copyright: "Taken from Sonniss.com - GDC 2023 - 344 AUdio - Epic Impacts Vol. 1"

Binary file not shown.

View File

@@ -186,6 +186,49 @@
True: { visible: true } True: { visible: true }
False: { visible: false } False: { visible: false }
- type: entity
name: force gun
parent:
- BaseItem
- PowerCellSlotMediumItem
id: WeaponForceGun
description: Manipulates gravity around objects to fling them at high velocities.
components:
- type: ForceGun
frequency: 15
dampingRatio: 4
massLimit: 50
lineColor: "#18a2d5"
soundLaunch:
path: /Audio/Weapons/soup.ogg
params:
volume: 2
- type: PowerCellDraw
- type: Sprite
sprite: Objects/Weapons/Guns/Launchers/force_gun.rsi
layers:
- state: base
- state: base-unshaded
map: [ "unshaded" ]
shader: unshaded
visible: false
- type: ToggleableLightVisuals
spriteLayer: unshaded
inhandVisuals:
left:
- state: inhand-left-unshaded
shader: unshaded
right:
- state: inhand-right-unshaded
shader: unshaded
- type: Appearance
- type: GenericVisualizer
visuals:
enum.TetherVisualsStatus.Key:
unshaded:
True: { visible: true }
False: { visible: false }
# Admeme # Admeme
- type: entity - type: entity
name: tether gun name: tether gun
@@ -226,6 +269,48 @@
True: { visible: true } True: { visible: true }
False: { visible: false } False: { visible: false }
- type: entity
name: force gun
parent: BaseItem
id: WeaponForceGunAdmin
suffix: Admin
description: Manipulates gravity around objects to fling them at high velocities.
components:
- type: ForceGun
canTetherAlive: true
canUnanchor: true
maxForce: 10000
massLimit: 10000
frequency: 15
dampingRatio: 4
throwForce: 50
throwDistance: 100
lineColor: "#18a2d5"
- type: Sprite
sprite: Objects/Weapons/Guns/Launchers/force_gun.rsi
layers:
- state: base
- state: base-unshaded
map: [ "unshaded" ]
shader: unshaded
visible: false
- type: ToggleableLightVisuals
spriteLayer: unshaded
inhandVisuals:
left:
- state: inhand-left-unshaded
shader: unshaded
right:
- state: inhand-right-unshaded
shader: unshaded
- type: Appearance
- type: GenericVisualizer
visuals:
enum.TetherVisualsStatus.Key:
unshaded:
True: { visible: true }
False: { visible: false }
- type: entity - type: entity
name: meteor launcher name: meteor launcher
parent: WeaponLauncherMultipleRocket parent: WeaponLauncherMultipleRocket

View File

@@ -239,6 +239,7 @@
- ClothingShoesBootsMag - ClothingShoesBootsMag
- NodeScanner - NodeScanner
- HolofanProjector - HolofanProjector
- WeaponForceGun
- WeaponTetherGun - WeaponTetherGun
- ClothingBackpackHolding - ClothingBackpackHolding
- ClothingBackpackSatchelHolding - ClothingBackpackSatchelHolding

View File

@@ -100,6 +100,15 @@
Plastic: 750 Plastic: 750
Plasma: 1000 Plasma: 1000
- type: latheRecipe
id: WeaponForceGun
result: WeaponForceGun
completetime: 5
materials:
Steel: 500
Glass: 400
Silver: 200
- type: latheRecipe - type: latheRecipe
id: WeaponTetherGun id: WeaponTetherGun
result: WeaponTetherGun result: WeaponTetherGun

View File

@@ -171,4 +171,5 @@
tier: 3 tier: 3
cost: 10000 cost: 10000
recipeUnlocks: recipeUnlocks:
- WeaponForceGun
- WeaponTetherGun - WeaponTetherGun

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,33 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Sprited by discord Kheprep#7153, modified by metalgearsloth",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "base"
},
{
"name": "base-unshaded"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
},
{
"name": "inhand-left-unshaded",
"directions": 4
},
{
"name": "inhand-right-unshaded",
"directions": 4
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB