From 789c5e0a2bb8c4dbe1fbea3be0375b7d7faa7462 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Tue, 29 Aug 2023 16:34:05 +1000 Subject: [PATCH] Add fultons (#18958) --- .../Chemistry/Components/InjectorComponent.cs | 10 - .../Items/Components/ItemStatusComponent.cs | 9 - Content.Client/Salvage/FultonSystem.cs | 118 +++++++++++ Content.Client/Storage/UI/StorageWindow.cs | 1 - Content.IntegrationTests/Tests/EntityTest.cs | 1 + Content.Server/Entry/IgnoredComponents.cs | 1 - Content.Server/Salvage/FultonSystem.cs | 70 +++++++ .../Salvage/SalvageSystem.Runner.cs | 10 +- Content.Shared/DoAfter/SharedDoAfterSystem.cs | 2 +- .../Foldable/SharedFoldableSystem.cs | 11 + .../Salvage/Fulton/FultonBeaconComponent.cs | 14 ++ .../Salvage/Fulton/FultonComponent.cs | 54 +++++ .../Salvage/Fulton/FultonedComponent.cs | 40 ++++ .../Salvage/Fulton/SharedFultonSystem.cs | 194 ++++++++++++++++++ Resources/Audio/Items/Mining/attributions.yml | 4 + .../Audio/Items/Mining/fultext_deploy.ogg | Bin 0 -> 14418 bytes .../Audio/Items/Mining/fultext_launch.ogg | Bin 0 -> 11001 bytes .../Locale/en-US/research/technologies.ftl | 1 + .../Locale/en-US/salvage/fulton-system.ftl | 7 + .../Objects/Materials/Sheets/glass.yml | 1 - .../Objects/Materials/Sheets/metal.yml | 1 - .../Objects/Materials/Sheets/other.yml | 1 - .../Entities/Objects/Materials/ingots.yml | 1 - .../Entities/Objects/Materials/materials.yml | 1 - .../Entities/Objects/Materials/ore.yml | 1 - .../Entities/Objects/Materials/parts.yml | 1 - .../Entities/Objects/Tools/fulton.yml | 108 ++++++++++ .../Entities/Objects/Tools/lighters.yml | 1 - .../Entities/Objects/Tools/welders.yml | 1 - .../Objects/Weapons/Melee/chainsaw.yml | 1 - .../Entities/Structures/Machines/lathe.yml | 2 + .../Prototypes/Recipes/Lathes/salvage.yml | 15 ++ .../Tools/fulton.rsi/extraction_pack.png | Bin 0 -> 1373 bytes .../Tools/fulton.rsi/extraction_point.png | Bin 0 -> 724 bytes .../fulton.rsi/extraction_point_light.png | Bin 0 -> 328 bytes .../Tools/fulton.rsi/folded_extraction.png | Bin 0 -> 303 bytes .../Objects/Tools/fulton.rsi/meta.json | 35 ++++ .../fulton_balloon.rsi/fulton_balloon.png | Bin 0 -> 2064 bytes .../fulton_balloon.rsi/fulton_expand.png | Bin 0 -> 4029 bytes .../Tools/fulton_balloon.rsi/meta.json | 25 +++ 40 files changed, 705 insertions(+), 37 deletions(-) delete mode 100644 Content.Client/Items/Components/ItemStatusComponent.cs create mode 100644 Content.Client/Salvage/FultonSystem.cs create mode 100644 Content.Server/Salvage/FultonSystem.cs create mode 100644 Content.Shared/Salvage/Fulton/FultonBeaconComponent.cs create mode 100644 Content.Shared/Salvage/Fulton/FultonComponent.cs create mode 100644 Content.Shared/Salvage/Fulton/FultonedComponent.cs create mode 100644 Content.Shared/Salvage/Fulton/SharedFultonSystem.cs create mode 100644 Resources/Audio/Items/Mining/attributions.yml create mode 100644 Resources/Audio/Items/Mining/fultext_deploy.ogg create mode 100644 Resources/Audio/Items/Mining/fultext_launch.ogg create mode 100644 Resources/Locale/en-US/salvage/fulton-system.ftl create mode 100644 Resources/Prototypes/Entities/Objects/Tools/fulton.yml create mode 100644 Resources/Prototypes/Recipes/Lathes/salvage.yml create mode 100644 Resources/Textures/Objects/Tools/fulton.rsi/extraction_pack.png create mode 100644 Resources/Textures/Objects/Tools/fulton.rsi/extraction_point.png create mode 100644 Resources/Textures/Objects/Tools/fulton.rsi/extraction_point_light.png create mode 100644 Resources/Textures/Objects/Tools/fulton.rsi/folded_extraction.png create mode 100644 Resources/Textures/Objects/Tools/fulton.rsi/meta.json create mode 100644 Resources/Textures/Objects/Tools/fulton_balloon.rsi/fulton_balloon.png create mode 100644 Resources/Textures/Objects/Tools/fulton_balloon.rsi/fulton_expand.png create mode 100644 Resources/Textures/Objects/Tools/fulton_balloon.rsi/meta.json diff --git a/Content.Client/Chemistry/Components/InjectorComponent.cs b/Content.Client/Chemistry/Components/InjectorComponent.cs index 46120c1b39..4d10517a11 100644 --- a/Content.Client/Chemistry/Components/InjectorComponent.cs +++ b/Content.Client/Chemistry/Components/InjectorComponent.cs @@ -1,15 +1,5 @@ -using Content.Client.Items.Components; -using Content.Client.Message; -using Content.Client.Stylesheets; using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; -using Robust.Shared.GameObjects; -using Robust.Shared.Localization; -using Robust.Shared.Timing; -using Robust.Shared.ViewVariables; namespace Content.Client.Chemistry.Components { diff --git a/Content.Client/Items/Components/ItemStatusComponent.cs b/Content.Client/Items/Components/ItemStatusComponent.cs deleted file mode 100644 index 34ab3bc9a0..0000000000 --- a/Content.Client/Items/Components/ItemStatusComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Robust.Shared.GameObjects; - -namespace Content.Client.Items.Components -{ - [RegisterComponent] - public sealed partial class ItemStatusComponent : Component - { - } -} diff --git a/Content.Client/Salvage/FultonSystem.cs b/Content.Client/Salvage/FultonSystem.cs new file mode 100644 index 0000000000..1ecebdfdf8 --- /dev/null +++ b/Content.Client/Salvage/FultonSystem.cs @@ -0,0 +1,118 @@ +using System.Numerics; +using Content.Shared.Salvage.Fulton; +using Content.Shared.Spawners.Components; +using JetBrains.Annotations; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Shared.Animations; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Utility; + +namespace Content.Client.Salvage; + +public sealed class FultonSystem : SharedFultonSystem +{ + [Dependency] private readonly ISerializationManager _serManager = default!; + [Dependency] private readonly AnimationPlayerSystem _player = default!; + + private static readonly TimeSpan AnimationDuration = TimeSpan.FromSeconds(0.4); + + private static readonly Animation InitialAnimation = new() + { + Length = AnimationDuration, + AnimationTracks = + { + new AnimationTrackSpriteFlick + { + LayerKey = FultonVisualLayers.Base, + KeyFrames = + { + new AnimationTrackSpriteFlick.KeyFrame(new RSI.StateId("fulton_expand"), 0f), + new AnimationTrackSpriteFlick.KeyFrame(new RSI.StateId("fulton_balloon"), 0.4f), + } + } + } + }; + + private static readonly Animation FultonAnimation = new() + { + Length = TimeSpan.FromSeconds(0.8f), + AnimationTracks = + { + new AnimationTrackComponentProperty() + { + ComponentType = typeof(SpriteComponent), + Property = nameof(SpriteComponent.Offset), + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(Vector2.Zero, 0f), + new AnimationTrackProperty.KeyFrame(new Vector2(0f, -0.3f), 0.3f), + new AnimationTrackProperty.KeyFrame(new Vector2(0f, 20f), 0.5f), + } + } + } + }; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnHandleState); + SubscribeNetworkEvent(OnFultonMessage); + } + + private void OnFultonMessage(FultonAnimationMessage ev) + { + if (Deleted(ev.Entity) || !TryComp(ev.Entity, out var entSprite)) + return; + + var animationEnt = Spawn(null, ev.Coordinates); + // TODO: Spawn fulton layer + var sprite = AddComp(animationEnt); + _serManager.CopyTo(entSprite, ref sprite, notNullableOverride: true); + + if (TryComp(ev.Entity, out var entAppearance)) + { + var appearance = AddComp(animationEnt); + _serManager.CopyTo(entAppearance, ref appearance, notNullableOverride: true); + } + + sprite.NoRotation = true; + var effectLayer = sprite.AddLayer(new SpriteSpecifier.Rsi(new ResPath("Objects/Tools/fulton_balloon.rsi"), "fulton_balloon")); + sprite.LayerSetOffset(effectLayer, EffectOffset + new Vector2(0f, 0.5f)); + + var despawn = AddComp(animationEnt); + despawn.Lifetime = 1.5f; + + _player.Play(animationEnt, FultonAnimation, "fulton-animation"); + } + + private void OnHandleState(EntityUid uid, FultonedComponent component, ref AfterAutoHandleStateEvent args) + { + UpdateAppearance(uid, component); + } + + protected override void UpdateAppearance(EntityUid uid, FultonedComponent component) + { + if (!component.Effect.IsValid()) + return; + + var startTime = component.NextFulton - component.FultonDuration; + var elapsed = Timing.CurTime - startTime; + + if (elapsed >= AnimationDuration) + { + return; + } + + _player.Play(component.Effect, InitialAnimation, "fulton"); + } + + [UsedImplicitly] + public enum FultonVisualLayers : byte + { + Base, + } + + +} diff --git a/Content.Client/Storage/UI/StorageWindow.cs b/Content.Client/Storage/UI/StorageWindow.cs index 521d1cf590..aee8b86c7a 100644 --- a/Content.Client/Storage/UI/StorageWindow.cs +++ b/Content.Client/Storage/UI/StorageWindow.cs @@ -3,7 +3,6 @@ using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; -using Content.Client.Items.Components; using Content.Client.Stylesheets; using Content.Client.UserInterface.Controls; using Content.Shared.IdentityManagement; diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 67923f5e7c..0966e984bb 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -311,6 +311,7 @@ namespace Content.IntegrationTests.Tests "DebrisFeaturePlacerController", // Above. "LoadedChunk", // Worldgen chunk loading malding. "BiomeSelection", // Whaddya know, requires config. + "DeployableBarrier", }; await using var pair = await PoolManager.GetServerClient(); diff --git a/Content.Server/Entry/IgnoredComponents.cs b/Content.Server/Entry/IgnoredComponents.cs index 3294b43030..e1d744da2d 100644 --- a/Content.Server/Entry/IgnoredComponents.cs +++ b/Content.Server/Entry/IgnoredComponents.cs @@ -7,7 +7,6 @@ namespace Content.Server.Entry "ConstructionGhost", "IconSmooth", "InteractionOutline", - "ItemStatus", "Marker", "GuidebookControlsTest", "GuideHelp", diff --git a/Content.Server/Salvage/FultonSystem.cs b/Content.Server/Salvage/FultonSystem.cs new file mode 100644 index 0000000000..b16339e05a --- /dev/null +++ b/Content.Server/Salvage/FultonSystem.cs @@ -0,0 +1,70 @@ +using System.Numerics; +using Content.Shared.Salvage.Fulton; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.Salvage; + +public sealed class FultonSystem : SharedFultonSystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnFultonedStartup); + SubscribeLocalEvent(OnFultonedShutdown); + } + + private void OnFultonedShutdown(EntityUid uid, FultonedComponent component, ComponentShutdown args) + { + Del(component.Effect); + component.Effect = EntityUid.Invalid; + } + + private void OnFultonedStartup(EntityUid uid, FultonedComponent component, ComponentStartup args) + { + if (Exists(component.Effect)) + return; + + component.Effect = Spawn(EffectProto, new EntityCoordinates(uid, EffectOffset)); + Dirty(uid, component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + var curTime = Timing.CurTime; + + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.NextFulton > curTime) + continue; + + Fulton(uid, comp); + } + } + + private void Fulton(EntityUid uid, FultonedComponent component) + { + if (!Deleted(component.Beacon)) + { + var xform = Transform(uid); + var oldCoords = xform.Coordinates; + var offset = _random.NextVector2(1.5f); + TransformSystem.SetCoordinates(uid, new EntityCoordinates(component.Beacon.Value, offset)); + + RaiseNetworkEvent(new FultonAnimationMessage() + { + Entity = uid, + Coordinates = oldCoords, + }); + } + + Audio.PlayPvs(component.Sound, uid); + RemCompDeferred(uid); + } +} diff --git a/Content.Server/Salvage/SalvageSystem.Runner.cs b/Content.Server/Salvage/SalvageSystem.Runner.cs index f94d71bf67..ce65ead928 100644 --- a/Content.Server/Salvage/SalvageSystem.Runner.cs +++ b/Content.Server/Salvage/SalvageSystem.Runner.cs @@ -155,24 +155,24 @@ public sealed partial class SalvageSystem { var remaining = comp.EndTime - _timing.CurTime; - if (comp.Stage < ExpeditionStage.FinalCountdown && remaining < TimeSpan.FromSeconds(30)) + if (comp.Stage < ExpeditionStage.FinalCountdown && remaining < TimeSpan.FromSeconds(45)) { comp.Stage = ExpeditionStage.FinalCountdown; - Dirty(comp); - Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-seconds", ("duration", TimeSpan.FromSeconds(30).Seconds))); + Dirty(uid, comp); + Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-seconds", ("duration", TimeSpan.FromSeconds(45).Seconds))); } else if (comp.Stage < ExpeditionStage.MusicCountdown && remaining < TimeSpan.FromMinutes(2)) { // TODO: Some way to play audio attached to a map for players. comp.Stream = _audio.PlayGlobal(comp.Sound, Filter.BroadcastMap(Comp(uid).MapId), true); comp.Stage = ExpeditionStage.MusicCountdown; - Dirty(comp); + Dirty(uid, comp); Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-minutes", ("duration", TimeSpan.FromMinutes(2).Minutes))); } else if (comp.Stage < ExpeditionStage.Countdown && remaining < TimeSpan.FromMinutes(5)) { comp.Stage = ExpeditionStage.Countdown; - Dirty(comp); + Dirty(uid, comp); Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-minutes", ("duration", TimeSpan.FromMinutes(5).Minutes))); } // Auto-FTL out any shuttles diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.cs index 350f6e4b27..4bb6005b1c 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.cs @@ -216,7 +216,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem doAfter.InitialItem = handsComponent.ActiveHandEntity; } - // Inital checks + // Initial checks if (ShouldCancel(doAfter, GetEntityQuery(), GetEntityQuery())) return false; diff --git a/Content.Shared/Foldable/SharedFoldableSystem.cs b/Content.Shared/Foldable/SharedFoldableSystem.cs index 2b2b6423d0..78e8d158db 100644 --- a/Content.Shared/Foldable/SharedFoldableSystem.cs +++ b/Content.Shared/Foldable/SharedFoldableSystem.cs @@ -59,6 +59,17 @@ public abstract class SharedFoldableSystem : EntitySystem args.Cancelled = true; } + /// + /// Returns false if the entity isn't foldable. + /// + public bool IsFolded(EntityUid uid, FoldableComponent? component = null) + { + if (!Resolve(uid, ref component)) + return false; + + return component.IsFolded; + } + /// /// Set the folded state of the given /// diff --git a/Content.Shared/Salvage/Fulton/FultonBeaconComponent.cs b/Content.Shared/Salvage/Fulton/FultonBeaconComponent.cs new file mode 100644 index 0000000000..94ddffb108 --- /dev/null +++ b/Content.Shared/Salvage/Fulton/FultonBeaconComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Salvage.Fulton; + +/// +/// Receives . +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class FultonBeaconComponent : Component +{ + [ViewVariables(VVAccess.ReadWrite), DataField("soundLink"), AutoNetworkedField] + public SoundSpecifier? LinkSound = new SoundPathSpecifier("/Audio/Items/beep.ogg"); +} diff --git a/Content.Shared/Salvage/Fulton/FultonComponent.cs b/Content.Shared/Salvage/Fulton/FultonComponent.cs new file mode 100644 index 0000000000..5493636776 --- /dev/null +++ b/Content.Shared/Salvage/Fulton/FultonComponent.cs @@ -0,0 +1,54 @@ +using Content.Shared.Whitelist; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Salvage.Fulton; + +/// +/// Applies to the target so they teleport to after a time. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class FultonComponent : Component +{ + /// + /// How long it takes to apply the fulton to an entity. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("applyDuration"), AutoNetworkedField] + public TimeSpan ApplyFultonDuration = TimeSpan.FromSeconds(3); + + /// + /// Linked fulton beacon. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("beacon")] + public EntityUid? Beacon; + + /// + /// Applies Removeable to the . + /// + [ViewVariables(VVAccess.ReadWrite), DataField("removeable"), AutoNetworkedField] + public bool Removeable = true; + + /// + /// How long the fulton will remain before teleporting to the beacon. + /// + [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", + } + }; + + /// + /// Sound that gets played when fulton is applied. + /// + /// + [ViewVariables(VVAccess.ReadWrite), DataField("soundFulton"), AutoNetworkedField] + public SoundSpecifier? FultonSound = new SoundPathSpecifier("/Audio/Items/Mining/fultext_deploy.ogg"); +} diff --git a/Content.Shared/Salvage/Fulton/FultonedComponent.cs b/Content.Shared/Salvage/Fulton/FultonedComponent.cs new file mode 100644 index 0000000000..3070249eee --- /dev/null +++ b/Content.Shared/Salvage/Fulton/FultonedComponent.cs @@ -0,0 +1,40 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared.Salvage.Fulton; + +/// +/// Marks an entity as pending being fultoned. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +public sealed partial class FultonedComponent : Component +{ + /// + /// Effect entity to delete upon removing the component. Only matters clientside. + /// + [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); + + /// + /// When the fulton is travelling to the beacon. + /// + [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. + /// + /// Can the fulton be removed. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("removeable")] + public bool Removeable = true; +} diff --git a/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs b/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs new file mode 100644 index 0000000000..7efa782c51 --- /dev/null +++ b/Content.Shared/Salvage/Fulton/SharedFultonSystem.cs @@ -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; + +/// +/// Provides extraction devices that teleports the attached entity after elapses to the linked beacon. +/// +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] public const string EffectProto = "FultonEffect"; + protected static readonly Vector2 EffectOffset = Vector2.Zero; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnFultonDoAfter); + + SubscribeLocalEvent(OnFultonUnpaused); + SubscribeLocalEvent>(OnFultonedGetVerbs); + SubscribeLocalEvent(OnFultonedExamine); + SubscribeLocalEvent(OnFultonContainerInserted); + + SubscribeLocalEvent(OnFultonInteract); + } + + private void OnFultonContainerInserted(EntityUid uid, FultonedComponent component, EntGotInsertedIntoContainerMessage args) + { + RemCompDeferred(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 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(uid); + } + + private void OnFultonDoAfter(FultonedDoAfterEvent args) + { + if (args.Cancelled || args.Target == null || !TryComp(args.Used, out var fulton)) + return; + + if (!_stack.Use(args.Used.Value, 1)) + { + return; + } + + var fultoned = AddComp(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(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(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. + /// + /// Tells clients to play the fulton animation. + /// + [Serializable, NetSerializable] + protected sealed class FultonAnimationMessage : EntityEventArgs + { + public EntityUid Entity; + public EntityCoordinates Coordinates; + } +} diff --git a/Resources/Audio/Items/Mining/attributions.yml b/Resources/Audio/Items/Mining/attributions.yml new file mode 100644 index 0000000000..847dba4351 --- /dev/null +++ b/Resources/Audio/Items/Mining/attributions.yml @@ -0,0 +1,4 @@ +- files: ["fultext_deploy.ogg", "fultext_launch.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Taken from tgstation" + source: "https://github.com/tgstation/tgstation/tree/893e5b0180f3fffe192d2e241325cfac937f5257/sound/items" diff --git a/Resources/Audio/Items/Mining/fultext_deploy.ogg b/Resources/Audio/Items/Mining/fultext_deploy.ogg new file mode 100644 index 0000000000000000000000000000000000000000..2433b9e0fe3c658bdfe889fb46290211d50cffb2 GIT binary patch literal 14418 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

7&_1|7#b_kwsan3 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 literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/research/technologies.ftl b/Resources/Locale/en-US/research/technologies.ftl index 44677ef890..9cd75a91c4 100644 --- a/Resources/Locale/en-US/research/technologies.ftl +++ b/Resources/Locale/en-US/research/technologies.ftl @@ -5,6 +5,7 @@ research-discipline-arsenal = Arsenal research-discipline-experimental = Experimental research-discipline-civilian-services = Civilian Services +research-technology-fulton = Fultons research-technology-salvage-equipment = Salvage Equipment research-technology-advanced-powercells = Advanced Powercells research-technology-compact-power = Compact Power diff --git a/Resources/Locale/en-US/salvage/fulton-system.ftl b/Resources/Locale/en-US/salvage/fulton-system.ftl new file mode 100644 index 0000000000..3d8e9e98c0 --- /dev/null +++ b/Resources/Locale/en-US/salvage/fulton-system.ftl @@ -0,0 +1,7 @@ +fulton-folded = Beacon needs unfolding +fulton-examine = {$time} seconds until extraction +fulton-linked = Linked beacon +fulton-not-found = No beacon found +fulton-invalid = Can't fulton +fulton-fultoned = Already fultoned +fulton-remove = Remove fulton diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml index f22eb93e86..2c1a8e628d 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml @@ -12,7 +12,6 @@ size: 30 - type: StaticPrice price: 0 - - type: ItemStatus - type: Tag tags: - Sheet diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml index d38cfd5240..fb08f7ad9b 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml @@ -11,7 +11,6 @@ size: 30 - type: StaticPrice price: 0 - - type: ItemStatus - type: Tag tags: - Sheet diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml index 85c659378a..3cb6f027a9 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml @@ -9,7 +9,6 @@ - type: Item sprite: Objects/Materials/Sheets/other.rsi size: 30 - - type: ItemStatus - type: Tag tags: - Sheet diff --git a/Resources/Prototypes/Entities/Objects/Materials/ingots.yml b/Resources/Prototypes/Entities/Objects/Materials/ingots.yml index 386d21beb9..61aefee332 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/ingots.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/ingots.yml @@ -11,7 +11,6 @@ size: 30 - type: StaticPrice price: 0 - - type: ItemStatus - type: Tag tags: - Ingot diff --git a/Resources/Prototypes/Entities/Objects/Materials/materials.yml b/Resources/Prototypes/Entities/Objects/Materials/materials.yml index 05961b355e..1a242db1af 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/materials.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/materials.yml @@ -9,7 +9,6 @@ - type: Item sprite: Objects/Materials/materials.rsi size: 30 - - type: ItemStatus - type: Tag tags: - DroneUsable diff --git a/Resources/Prototypes/Entities/Objects/Materials/ore.yml b/Resources/Prototypes/Entities/Objects/Materials/ore.yml index bd639fd51a..f27d1b770e 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/ore.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/ore.yml @@ -9,7 +9,6 @@ - type: Item sprite: Objects/Materials/ore.rsi size: 60 - - type: ItemStatus - type: Tag tags: - Ore diff --git a/Resources/Prototypes/Entities/Objects/Materials/parts.yml b/Resources/Prototypes/Entities/Objects/Materials/parts.yml index d07f511fdf..e75704c2a4 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/parts.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/parts.yml @@ -8,7 +8,6 @@ state: rods - type: Item sprite: Objects/Materials/parts.rsi - - type: ItemStatus - type: Tag tags: - DroneUsable diff --git a/Resources/Prototypes/Entities/Objects/Tools/fulton.yml b/Resources/Prototypes/Entities/Objects/Tools/fulton.yml new file mode 100644 index 0000000000..e22cae19cd --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Tools/fulton.yml @@ -0,0 +1,108 @@ +# Stack +- type: stack + id: Fulton + name: fulton + icon: + sprite: /Textures/Objects/Tools/fulton.rsi + state: extraction_pack + spawn: Fulton1 + maxCount: 10 + itemSize: 2 + +# Entities +- type: entity + id: FultonBeacon + parent: BaseFoldable + name: fulton beacon + description: Beacon to receive fulton extractions. + components: + - type: Physics + bodyType: Dynamic + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.4,0.25,0.1" + density: 20 + mask: + - Impassable + - type: Item + size: 30 + - type: Foldable + - type: Clickable + - type: InteractionOutline + - type: FultonBeacon + - type: Appearance + - type: GenericVisualizer + visuals: + enum.FoldedVisuals.State: + foldedLayer: + True: { state: folded_extraction } + False: { state: extraction_point } + - type: Sprite + sprite: Objects/Tools/fulton.rsi + drawdepth: SmallObjects + noRot: true + layers: + - state: extraction_point + map: [ "foldedLayer" ] + +- type: entity + id: Fulton + parent: BaseItem + suffix: Full + name: fulton + description: Used to extract containers, items, or forcibly recruit people into your base of operations. + components: + - type: Fulton + - type: Item + size: 20 + - type: Stack + stackType: Fulton + count: 10 + - type: Sprite + drawdepth: SmallObjects + sprite: Objects/Tools/fulton.rsi + state: extraction_pack + - type: Damageable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + +- type: entity + id: Fulton1 + parent: Fulton + name: fulton + suffix: One + components: + - type: Item + size: 2 + - type: Stack + count: 1 + +- type: entity + id: FultonEffect + noSpawn: true + name: fulton effect + components: + - type: TimedDespawn + lifetime: 60 + - type: Sprite + drawdepth: Effects + noRot: true + offset: 0,0.5 + layers: + - map: [ "enum.FultonVisualLayers.Base" ] + sprite: Objects/Tools/fulton_balloon.rsi + state: fulton_balloon + - type: Tag + tags: + - HideContextMenu + - type: AnimationPlayer diff --git a/Resources/Prototypes/Entities/Objects/Tools/lighters.yml b/Resources/Prototypes/Entities/Objects/Tools/lighters.yml index 8dd4fa0c6a..e3422f2a4a 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/lighters.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/lighters.yml @@ -44,7 +44,6 @@ sprite: Objects/Tools/lighters.rsi heldPrefix: off - type: ItemCooldown - - type: ItemStatus - type: RefillableSolution solution: Welder - type: SolutionContainerManager diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index 710e7d5be0..3a105a2994 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -33,7 +33,6 @@ damage: types: Blunt: 5 #i mean... i GUESS you could use it like that - - type: ItemStatus - type: RefillableSolution solution: Welder - type: SolutionContainerManager diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/chainsaw.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/chainsaw.yml index 1e108c2dd7..387913715c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/chainsaw.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/chainsaw.yml @@ -34,7 +34,6 @@ size: 50 sprite: Objects/Weapons/Melee/chainsaw.rsi - type: DisarmMalus - - type: ItemStatus - type: RefillableSolution solution: Welder - type: SolutionContainerManager diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 72b01b266c..7832999464 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -97,6 +97,8 @@ - TRayScanner - GasAnalyzer - UtilityBelt + - Fulton + - FultonBeacon - Pickaxe - ModularReceiver - AppraisalTool diff --git a/Resources/Prototypes/Recipes/Lathes/salvage.yml b/Resources/Prototypes/Recipes/Lathes/salvage.yml new file mode 100644 index 0000000000..9cec605b3e --- /dev/null +++ b/Resources/Prototypes/Recipes/Lathes/salvage.yml @@ -0,0 +1,15 @@ +- type: latheRecipe + id: Fulton + result: Fulton1 + completetime: 5 + materials: + Cloth: 200 + +- type: latheRecipe + id: FultonBeacon + result: FultonBeacon + completetime: 5 + materials: + Steel: 1000 + Glass: 500 + # If they get spammed make it cost silver. diff --git a/Resources/Textures/Objects/Tools/fulton.rsi/extraction_pack.png b/Resources/Textures/Objects/Tools/fulton.rsi/extraction_pack.png new file mode 100644 index 0000000000000000000000000000000000000000..bdaf6822024c752d5125184afa3cc7a631e54669 GIT binary patch literal 1373 zcmV-j1)}g-m9GkQ5fldl<_7-21=&<|i2^#= z_QOOMH={Ax+(L}}GnpY-vSfeQvSco1i9yZOuqZMk;zyh)!T<%GK^=k=mO13ZQYqB- z-rKWt4zz<&q$RRt$)4n;v6XOi4-c zlq5ob1-*|&a!@a{ zI$DFe4iXgscs$TAR0FaEXgbtu4MD{WSpiqO4J2|d$SgDHpi?B4*sg^rPu=V^cv)eZ z(PC?z83J4d_m15Z5p%`v;ZfmbwSd`XAj|+Met}4CYrj7;1USC`-G+=F6JE(k2emsO zF-0h0McH&4J)NDf=}uJ5H_aS@?Nz5lbstn+G{ay*ylWmDOpUAFVQl|#WbV_wh4gyk zmED=kR(|zd2&`YX!q@qR*~7cf@Cn+F6CU8q!DADS+h?DDO+G0sqDM;if4Ozru1SyM zGYRBmXHZSoS!F>_OR6vym(&NcI+%1McKZN~vy8BqO~y5EEJb@q53H!VezqQ)3v#UGl{F&Hxt|(C z>x4VEzC}AaAA;}rY)uR1u#1Lm6b3~R2_D#bL=Q4b5+t4VBbGdIp+)#`aDTpTH6wkg z=B9}E-I3GdNbucn-BLi09IYS$6;L&e-K>Azw4+~n9T>%!Km7KU$>{5lxQ~CHgRkdt zCIj`l11sQzG`b}h>ukF2g(MTB#@egH!8#u-em_1|wP+}cZ1gfkxc_Xk$-h1j8h*Y^ zU(3pwyaqV#wAzrHy#!_F8gb!j6LNDhg%nPwdz_ybGdn9u=66FBWp4J%sJqeP_u2OT zyEH#9A4$onXl`!8*yIxkw6=F4eMvHYX}OQgS2a=TPo6ko>+1E&3e{yrGH}1L(^v3b z-U_fWx_j-at`T{_rAsoa4b`LJw}*ZHc-6JHNa^8|lPVHW0Zy^v{552~l!m%r@1o&m z3%lYV#YV?MWhY|OhLuoRHF#7Vb=U6*QrsacB~7g3@NHx&j3dC=vJaZHI00=BxZ0x5Y_HI^VR=fLpFkK(?j4i# z@8I(s+@G&|(A|e%5s-k3U9oL_-H3^f95Hw`$o#e=6WwL1gSTX#FJIT|uuq==mu(fh z1C}SGp{@Usu<-<30wRy}0Uw0E+%Ao@W{#woIP~`0gOWcW0UrsTHzGXJ*N7-9a6*h! zV~1Svv0%hB%|jJ-`j~}!7zblr-`${-PfEZ?f-5mP90@oy!Cx6OZyZBDXgu>e4mH?x f@ZbG!_`L8ZjDah*a>nx700000NkvXXu0mjfWi5mL literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tools/fulton.rsi/extraction_point.png b/Resources/Textures/Objects/Tools/fulton.rsi/extraction_point.png new file mode 100644 index 0000000000000000000000000000000000000000..7bdfd4ef702366b5374d8401faeb63f0bf2f4f5c GIT binary patch literal 724 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tools/fulton_balloon.rsi/fulton_expand.png b/Resources/Textures/Objects/Tools/fulton_balloon.rsi/fulton_expand.png new file mode 100644 index 0000000000000000000000000000000000000000..6b7af2daef8a8a1525272a8a78541799bef17460 GIT binary patch literal 4029 zcmV;u4?^&XP)$|_bxB@zvsW_+w1g=(GaV-ml=; zcWn(M5-Uh7ZFz?D)9D1b9D9!Q^6J}o|9++V+SRLkH+K)7L|ofYRG7_WXJ=!SufJYG zbSeQ7uE%h0?ry5@_I_@)&Q z9Ph(9ckfof=kvNXsv04a*+H$=AUit?)Od=s(b2Gz$s8jNe$CAZd_^Zu=oA7_Y6z=2bS zhldN@-FqP+As)+@FBQ^uYW&u%JLowsUHTa-RxW4Pf3kCj&V6?Z0n}#v8HA3uJYEq+3wfc<^y)MuCw+V=1Rhq;$toc;f^1_%UZEZ&zd|C^adZ#UR=v4NtZ z0)Ji{jBD4fV)g1Z9Pt94ySoRwe*OAQJacn?wJv>k3IV3in1?N4Tk-KHJ9%MSx1plK zhKOQ;vuC4`k(q@qQX8H^QH{jJ1lZWvprSlaU8zuR*QM`HA%L&1FOEd+%O;0vH&+*r z%san8KwLdJEdy>nT=2?*`6TW?hNX=ZyLauu%vsZF-~8))e;+V#*qXNbHdp|<&3v)@ zOGvv&s|l`H)VLHy9IfYqs|A@C&zJ;KLix3`uhz%@$$EPqhn)BDz);`2PIpA5n`Z7oWXJIA(7D7br@D=aKz z#)cmV=RSJi^7LGzuHpD*Ry=BaM~aY+SW3^)_ zC@28s*Qam21bEaq5(+RCsPR+;-_a8o{|~2*an1SZdB8K%*#sPpI7oIPAD#MbS1K%( zN*_8m#_Tu8;332C&rPejci#JeM~%G_cYzz}GmiJguAT2xRaG(m#DwdZ|LPJJg1Ypn zmjKi_8s|TqJySPn%1jA$`HSa&;QYo1JmmVuS_HNHA^9~;PmJ590Wi}Bil~Qkh zgF}EC|JlyXr80XNa{GN$02=>grDaCV{aeGeill`c`u6q0f>#%D`T2KA zXpA(95XRuwNr0U@HgOt_8jJqCl-&-Om6ft~D~TBQitoYP+=AudBC!Bo{raQeZYCFT zXkUkU#GF~BFQ-i4E$1u*%4>1>iB5v0m{rV3?b#)b_Hnvdl z)TpVx&z{%I<#H?uS&p7P-MQ({|EXOfQ2w!g6OG3IL;)yyU#~&zbrXH{6c8{Kaj`LQ z=;{QitqiHjN$Bp{6O9c@NV`~}x~i7kl2*Wu7B8q&7&dGqjvd)+un4B$Z&d*5_Pu-e zM@niYJUrY*nnJm^dzy?4`pND6`+T!C(>`Rki=VG z2t^XIAn~ZXVfUBcfL3tWSWB=aY#UT+6+)K$1%_ZjP>4zRTNGgP2k&x<@>0}PRg+FM z$jZ*ajrePD>*)%)g9C(OD?}bWid1qZ78JAqCd3>-KVb#=Aq(cKBVKHGv9=e+`vPy}}};14#3!P~13CQM-c=p=$E`5zO2 z#-2bR#q7Crap>Sdc(^*@$gv|BI%pW|?d{;~>W&HH#}aou4m(>Z4t;-u4dC?2C{$Kf zk=s)~_Lh@v`5zNNBr?aCF_TeTT7lY{YGh|;5cj)}s+u~Yv^Q=fCXj8$9(_o{k)NA~in3zd zD=NZ&zW#!~Dra#h5{aN9+fZ6sG7{orVJ;A&zOG&;0Zh%`;u>)Bf!7xMdXnv z1T6@{ci-*9{{3bTT~|dGDNj!iIM@sD&E7)@ockQMZrh2*#zvU&=~G-0S&*VIch+p2 zyBN#rdUSy)`CAm=xn~2A@ylH(>niX<;B>t7$N5cv^YrS6&p!JUp1nNKon&Ka=^411 zP{PQ2IQPN)mq)YxI5)qL&EkvmW}-q-iPwS`;b6o*odhr?e~SX#&dG+gjUD)AJZ#&x z11F9}!ou7f)x;&OtgOh^6NL0z>FnBqd!=MW?t|5Dzl%%fquFXaVBjFw*~@U_W(s6d z9|Zqp8J?d$gUyI8FeU$E0z@1>&kI@Bi}N2h3Q6#Oj; z(0{;Cvn!V`aNi#e$GwsgEL*Y&XHWkCOG_(c-^wKM<%Di>Tl|upkNR3AQ=YmRkNk7! zDn|;rrn;sc!HZ^)tw{-eLHOmS;&1h4NMdQFxpMg$->ZLL;({My#mZIqaPvmkk=RQ} zNI+Q$k)! z*!-;uK)vAlwHWSiZ>=EdCw5RIFXQ4i0h`9u;xe@G%BI73u(f<&*fIC;o%$m-h#mt$jD`@)5b89A-m>fzeEsZg@Lv;U|8gujFs zCntwoOZ<;ksJ1ox6m1HyCN%6ZH(veT8dj)y=4>>p)uW{{WBtZA6-qWJE7by_)NDiO zO1)#h6@F@nP}@cgC7-5i=xjd_hJo-G6Mnfa{5m}r*LndpN?EVq?%cWkP@$w2%?lMf zV)mQy3m)*xwZ^se8(n6fLMH*}EKoJ+H-W~<$%)^*P?8oX(eq|5$h9Z-pTytN-wD6N zqit=)z7~eO8sk5yP*SB*Yc_wd0doT9(SgI%pdLc_9aLQuCR{BpuyN%*C$ zTqf0Z_9^rdAY>W)e|g-44_p09OY%5-haQZO42+}hZ!Bon0&-ZTZF_{jlJM&k`&#H- zC`kv<-@EUx?`Db1(*e+9S}1w-dLkR7u`5Ohzs$tUR);5t*IEb%Z;ZPVZ$|hHwxiuuV#znIRJ*$NvBqxzwVEsVaLY%_UR$^z($GE(7U0;? z!<<+w!GfU0*7U!yni`a!J~9vAyC;(HSJ3n6GiI4?4WML{bmGKuNK4CrsqqNEts%x! jv?~M*CG9A51pog4{k4cYC|sZJ00000NkvXXu0mjf*^1vs literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tools/fulton_balloon.rsi/meta.json b/Resources/Textures/Objects/Tools/fulton_balloon.rsi/meta.json new file mode 100644 index 0000000000..36df097eb4 --- /dev/null +++ b/Resources/Textures/Objects/Tools/fulton_balloon.rsi/meta.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/tgstation/tgstation/blob/a7a67c81afc99d77483c84878173822ee8cbeecd/icons/obj/fulton.dmi", + "size": { + "x": 32, + "y": 41 + }, + "states": [ + { + "name": "fulton_balloon" + }, + { + "name": "fulton_expand", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} \ No newline at end of file