Add fultons (#18958)
This commit is contained in:
14
Content.Shared/Salvage/Fulton/FultonBeaconComponent.cs
Normal file
14
Content.Shared/Salvage/Fulton/FultonBeaconComponent.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Salvage.Fulton;
|
||||
|
||||
/// <summary>
|
||||
/// Receives <see cref="FultonedComponent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class FultonBeaconComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("soundLink"), AutoNetworkedField]
|
||||
public SoundSpecifier? LinkSound = new SoundPathSpecifier("/Audio/Items/beep.ogg");
|
||||
}
|
||||
54
Content.Shared/Salvage/Fulton/FultonComponent.cs
Normal file
54
Content.Shared/Salvage/Fulton/FultonComponent.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Salvage.Fulton;
|
||||
|
||||
/// <summary>
|
||||
/// Applies <see cref="FultonedComponent"/> to the target so they teleport to <see cref="FultonBeaconComponent"/> after a time.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class FultonComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How long it takes to apply the fulton to an entity.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("applyDuration"), AutoNetworkedField]
|
||||
public TimeSpan ApplyFultonDuration = TimeSpan.FromSeconds(3);
|
||||
|
||||
/// <summary>
|
||||
/// Linked fulton beacon.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("beacon")]
|
||||
public EntityUid? Beacon;
|
||||
|
||||
/// <summary>
|
||||
/// Applies Removeable to the <see cref="FultonedComponent"/>.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("removeable"), AutoNetworkedField]
|
||||
public bool Removeable = true;
|
||||
|
||||
/// <summary>
|
||||
/// How long the fulton will remain before teleporting to the beacon.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("duration")]
|
||||
public TimeSpan FultonDuration = TimeSpan.FromSeconds(45);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("whitelist"), AutoNetworkedField]
|
||||
public EntityWhitelist? Whitelist = new()
|
||||
{
|
||||
Components = new[]
|
||||
{
|
||||
"EntityStorage",
|
||||
"Item",
|
||||
"ReagentTank",
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Sound that gets played when fulton is applied.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("soundFulton"), AutoNetworkedField]
|
||||
public SoundSpecifier? FultonSound = new SoundPathSpecifier("/Audio/Items/Mining/fultext_deploy.ogg");
|
||||
}
|
||||
40
Content.Shared/Salvage/Fulton/FultonedComponent.cs
Normal file
40
Content.Shared/Salvage/Fulton/FultonedComponent.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared.Salvage.Fulton;
|
||||
|
||||
/// <summary>
|
||||
/// Marks an entity as pending being fultoned.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||
public sealed partial class FultonedComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Effect entity to delete upon removing the component. Only matters clientside.
|
||||
/// </summary>
|
||||
[ViewVariables, DataField("effect"), AutoNetworkedField]
|
||||
public EntityUid Effect { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("beacon")]
|
||||
public EntityUid? Beacon;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("fultonDuration"), AutoNetworkedField]
|
||||
public TimeSpan FultonDuration = TimeSpan.FromSeconds(45);
|
||||
|
||||
/// <summary>
|
||||
/// When the fulton is travelling to the beacon.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("nextFulton", customTypeSerializer:typeof(TimeOffsetSerializer)), AutoNetworkedField]
|
||||
public TimeSpan NextFulton;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("sound"), AutoNetworkedField]
|
||||
public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Items/Mining/fultext_launch.ogg");
|
||||
|
||||
// Mainly for admemes.
|
||||
/// <summary>
|
||||
/// Can the fulton be removed.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("removeable")]
|
||||
public bool Removeable = true;
|
||||
}
|
||||
194
Content.Shared/Salvage/Fulton/SharedFultonSystem.cs
Normal file
194
Content.Shared/Salvage/Fulton/SharedFultonSystem.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Foldable;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Salvage.Fulton;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extraction devices that teleports the attached entity after <see cref="FultonDuration"/> elapses to the linked beacon.
|
||||
/// </summary>
|
||||
public abstract partial class SharedFultonSystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
||||
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedFoldableSystem _foldable = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedStackSystem _stack = default!;
|
||||
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>] public const string EffectProto = "FultonEffect";
|
||||
protected static readonly Vector2 EffectOffset = Vector2.Zero;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<FultonedDoAfterEvent>(OnFultonDoAfter);
|
||||
|
||||
SubscribeLocalEvent<FultonedComponent, EntityUnpausedEvent>(OnFultonUnpaused);
|
||||
SubscribeLocalEvent<FultonedComponent, GetVerbsEvent<InteractionVerb>>(OnFultonedGetVerbs);
|
||||
SubscribeLocalEvent<FultonedComponent, ExaminedEvent>(OnFultonedExamine);
|
||||
SubscribeLocalEvent<FultonedComponent, EntGotInsertedIntoContainerMessage>(OnFultonContainerInserted);
|
||||
|
||||
SubscribeLocalEvent<FultonComponent, AfterInteractEvent>(OnFultonInteract);
|
||||
}
|
||||
|
||||
private void OnFultonContainerInserted(EntityUid uid, FultonedComponent component, EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
RemCompDeferred<FultonedComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnFultonedExamine(EntityUid uid, FultonedComponent component, ExaminedEvent args)
|
||||
{
|
||||
var remaining = component.NextFulton + _metadata.GetPauseTime(uid) - Timing.CurTime;
|
||||
var message = Loc.GetString("fulton-examine", ("time", $"{remaining.TotalSeconds:0.00}"));
|
||||
|
||||
args.PushText(message);
|
||||
}
|
||||
|
||||
private void OnFultonedGetVerbs(EntityUid uid, FultonedComponent component, GetVerbsEvent<InteractionVerb> args)
|
||||
{
|
||||
args.Verbs.Add(new InteractionVerb()
|
||||
{
|
||||
Text = Loc.GetString("fulton-remove"),
|
||||
Act = () =>
|
||||
{
|
||||
Unfulton(uid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void Unfulton(EntityUid uid, FultonedComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false) || !component.Removeable)
|
||||
return;
|
||||
|
||||
RemCompDeferred<FultonedComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnFultonDoAfter(FultonedDoAfterEvent args)
|
||||
{
|
||||
if (args.Cancelled || args.Target == null || !TryComp<FultonComponent>(args.Used, out var fulton))
|
||||
return;
|
||||
|
||||
if (!_stack.Use(args.Used.Value, 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var fultoned = AddComp<FultonedComponent>(args.Target.Value);
|
||||
fultoned.Beacon = fulton.Beacon;
|
||||
fultoned.NextFulton = Timing.CurTime + fulton.FultonDuration;
|
||||
fultoned.FultonDuration = fulton.FultonDuration;
|
||||
fultoned.Removeable = fulton.Removeable;
|
||||
UpdateAppearance(args.Target.Value, fultoned);
|
||||
Dirty(args.Target.Value, fultoned);
|
||||
Audio.PlayPredicted(fulton.FultonSound, args.Target.Value, args.User);
|
||||
}
|
||||
|
||||
private void OnFultonUnpaused(EntityUid uid, FultonedComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextFulton += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnFultonInteract(EntityUid uid, FultonComponent component, AfterInteractEvent args)
|
||||
{
|
||||
if (args.Target == null || args.Handled)
|
||||
return;
|
||||
|
||||
if (TryComp<FultonBeaconComponent>(args.Target, out var beacon))
|
||||
{
|
||||
if (!_foldable.IsFolded(args.Target.Value))
|
||||
{
|
||||
component.Beacon = args.Target.Value;
|
||||
Audio.PlayPredicted(beacon.LinkSound, uid, args.User);
|
||||
_popup.PopupClient(Loc.GetString("fulton-linked"), uid, args.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
component.Beacon = EntityUid.Invalid;
|
||||
_popup.PopupClient(Loc.GetString("fulton-folded"), uid, args.User);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Deleted(component.Beacon))
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("fulton-not-found"), uid, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CanFulton(args.Target.Value, uid, component))
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("fulton-invalid"), uid, uid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasComp<FultonedComponent>(args.Target))
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("fulton-fultoned"), uid, uid);
|
||||
return;
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
var ev = new FultonedDoAfterEvent();
|
||||
_doAfter.TryStartDoAfter(
|
||||
new DoAfterArgs(args.User, component.ApplyFultonDuration, ev, args.Target, args.Target, args.Used)
|
||||
{
|
||||
CancelDuplicate = true,
|
||||
MovementThreshold = 0.5f,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnTargetMove = true,
|
||||
Broadcast = true,
|
||||
NeedHand = true,
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual void UpdateAppearance(EntityUid uid, FultonedComponent fultoned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private bool CanFulton(EntityUid targetUid, EntityUid uid, FultonComponent component)
|
||||
{
|
||||
if (Transform(targetUid).Anchored)
|
||||
return false;
|
||||
|
||||
if (component.Whitelist?.IsValid(targetUid, EntityManager) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
private sealed partial class FultonedDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
|
||||
// Animations aren't really good for networking hence this.
|
||||
/// <summary>
|
||||
/// Tells clients to play the fulton animation.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class FultonAnimationMessage : EntityEventArgs
|
||||
{
|
||||
public EntityUid Entity;
|
||||
public EntityCoordinates Coordinates;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user