Add fultons (#18958)

This commit is contained in:
metalgearsloth
2023-08-29 16:34:05 +10:00
committed by GitHub
parent 9ffd3444a2
commit 789c5e0a2b
40 changed files with 705 additions and 37 deletions

View File

@@ -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.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; 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 namespace Content.Client.Chemistry.Components
{ {

View File

@@ -1,9 +0,0 @@
using Robust.Shared.GameObjects;
namespace Content.Client.Items.Components
{
[RegisterComponent]
public sealed partial class ItemStatusComponent : Component
{
}
}

View File

@@ -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<FultonedComponent, AfterAutoHandleStateEvent>(OnHandleState);
SubscribeNetworkEvent<FultonAnimationMessage>(OnFultonMessage);
}
private void OnFultonMessage(FultonAnimationMessage ev)
{
if (Deleted(ev.Entity) || !TryComp<SpriteComponent>(ev.Entity, out var entSprite))
return;
var animationEnt = Spawn(null, ev.Coordinates);
// TODO: Spawn fulton layer
var sprite = AddComp<SpriteComponent>(animationEnt);
_serManager.CopyTo(entSprite, ref sprite, notNullableOverride: true);
if (TryComp<AppearanceComponent>(ev.Entity, out var entAppearance))
{
var appearance = AddComp<AppearanceComponent>(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<TimedDespawnComponent>(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,
}
}

View File

@@ -3,7 +3,6 @@ using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Content.Client.Items.Components;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Controls;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;

View File

@@ -311,6 +311,7 @@ namespace Content.IntegrationTests.Tests
"DebrisFeaturePlacerController", // Above. "DebrisFeaturePlacerController", // Above.
"LoadedChunk", // Worldgen chunk loading malding. "LoadedChunk", // Worldgen chunk loading malding.
"BiomeSelection", // Whaddya know, requires config. "BiomeSelection", // Whaddya know, requires config.
"DeployableBarrier",
}; };
await using var pair = await PoolManager.GetServerClient(); await using var pair = await PoolManager.GetServerClient();

View File

@@ -7,7 +7,6 @@ namespace Content.Server.Entry
"ConstructionGhost", "ConstructionGhost",
"IconSmooth", "IconSmooth",
"InteractionOutline", "InteractionOutline",
"ItemStatus",
"Marker", "Marker",
"GuidebookControlsTest", "GuidebookControlsTest",
"GuideHelp", "GuideHelp",

View File

@@ -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<FultonedComponent, ComponentStartup>(OnFultonedStartup);
SubscribeLocalEvent<FultonedComponent, ComponentShutdown>(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<FultonedComponent>();
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<FultonedComponent>(uid);
}
}

View File

@@ -155,24 +155,24 @@ public sealed partial class SalvageSystem
{ {
var remaining = comp.EndTime - _timing.CurTime; 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; comp.Stage = ExpeditionStage.FinalCountdown;
Dirty(comp); Dirty(uid, comp);
Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-seconds", ("duration", TimeSpan.FromSeconds(30).Seconds))); Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-seconds", ("duration", TimeSpan.FromSeconds(45).Seconds)));
} }
else if (comp.Stage < ExpeditionStage.MusicCountdown && remaining < TimeSpan.FromMinutes(2)) else if (comp.Stage < ExpeditionStage.MusicCountdown && remaining < TimeSpan.FromMinutes(2))
{ {
// TODO: Some way to play audio attached to a map for players. // TODO: Some way to play audio attached to a map for players.
comp.Stream = _audio.PlayGlobal(comp.Sound, Filter.BroadcastMap(Comp<MapComponent>(uid).MapId), true); comp.Stream = _audio.PlayGlobal(comp.Sound, Filter.BroadcastMap(Comp<MapComponent>(uid).MapId), true);
comp.Stage = ExpeditionStage.MusicCountdown; comp.Stage = ExpeditionStage.MusicCountdown;
Dirty(comp); Dirty(uid, comp);
Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-minutes", ("duration", TimeSpan.FromMinutes(2).Minutes))); Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-minutes", ("duration", TimeSpan.FromMinutes(2).Minutes)));
} }
else if (comp.Stage < ExpeditionStage.Countdown && remaining < TimeSpan.FromMinutes(5)) else if (comp.Stage < ExpeditionStage.Countdown && remaining < TimeSpan.FromMinutes(5))
{ {
comp.Stage = ExpeditionStage.Countdown; comp.Stage = ExpeditionStage.Countdown;
Dirty(comp); Dirty(uid, comp);
Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-minutes", ("duration", TimeSpan.FromMinutes(5).Minutes))); Announce(uid, Loc.GetString("salvage-expedition-announcement-countdown-minutes", ("duration", TimeSpan.FromMinutes(5).Minutes)));
} }
// Auto-FTL out any shuttles // Auto-FTL out any shuttles

View File

@@ -216,7 +216,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
doAfter.InitialItem = handsComponent.ActiveHandEntity; doAfter.InitialItem = handsComponent.ActiveHandEntity;
} }
// Inital checks // Initial checks
if (ShouldCancel(doAfter, GetEntityQuery<TransformComponent>(), GetEntityQuery<HandsComponent>())) if (ShouldCancel(doAfter, GetEntityQuery<TransformComponent>(), GetEntityQuery<HandsComponent>()))
return false; return false;

View File

@@ -59,6 +59,17 @@ public abstract class SharedFoldableSystem : EntitySystem
args.Cancelled = true; args.Cancelled = true;
} }
/// <summary>
/// Returns false if the entity isn't foldable.
/// </summary>
public bool IsFolded(EntityUid uid, FoldableComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
return component.IsFolded;
}
/// <summary> /// <summary>
/// Set the folded state of the given <see cref="FoldableComponent"/> /// Set the folded state of the given <see cref="FoldableComponent"/>
/// </summary> /// </summary>

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

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

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

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

View File

@@ -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"

Binary file not shown.

Binary file not shown.

View File

@@ -5,6 +5,7 @@ research-discipline-arsenal = Arsenal
research-discipline-experimental = Experimental research-discipline-experimental = Experimental
research-discipline-civilian-services = Civilian Services research-discipline-civilian-services = Civilian Services
research-technology-fulton = Fultons
research-technology-salvage-equipment = Salvage Equipment research-technology-salvage-equipment = Salvage Equipment
research-technology-advanced-powercells = Advanced Powercells research-technology-advanced-powercells = Advanced Powercells
research-technology-compact-power = Compact Power research-technology-compact-power = Compact Power

View File

@@ -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

View File

@@ -12,7 +12,6 @@
size: 30 size: 30
- type: StaticPrice - type: StaticPrice
price: 0 price: 0
- type: ItemStatus
- type: Tag - type: Tag
tags: tags:
- Sheet - Sheet

View File

@@ -11,7 +11,6 @@
size: 30 size: 30
- type: StaticPrice - type: StaticPrice
price: 0 price: 0
- type: ItemStatus
- type: Tag - type: Tag
tags: tags:
- Sheet - Sheet

View File

@@ -9,7 +9,6 @@
- type: Item - type: Item
sprite: Objects/Materials/Sheets/other.rsi sprite: Objects/Materials/Sheets/other.rsi
size: 30 size: 30
- type: ItemStatus
- type: Tag - type: Tag
tags: tags:
- Sheet - Sheet

View File

@@ -11,7 +11,6 @@
size: 30 size: 30
- type: StaticPrice - type: StaticPrice
price: 0 price: 0
- type: ItemStatus
- type: Tag - type: Tag
tags: tags:
- Ingot - Ingot

View File

@@ -9,7 +9,6 @@
- type: Item - type: Item
sprite: Objects/Materials/materials.rsi sprite: Objects/Materials/materials.rsi
size: 30 size: 30
- type: ItemStatus
- type: Tag - type: Tag
tags: tags:
- DroneUsable - DroneUsable

View File

@@ -9,7 +9,6 @@
- type: Item - type: Item
sprite: Objects/Materials/ore.rsi sprite: Objects/Materials/ore.rsi
size: 60 size: 60
- type: ItemStatus
- type: Tag - type: Tag
tags: tags:
- Ore - Ore

View File

@@ -8,7 +8,6 @@
state: rods state: rods
- type: Item - type: Item
sprite: Objects/Materials/parts.rsi sprite: Objects/Materials/parts.rsi
- type: ItemStatus
- type: Tag - type: Tag
tags: tags:
- DroneUsable - DroneUsable

View File

@@ -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

View File

@@ -44,7 +44,6 @@
sprite: Objects/Tools/lighters.rsi sprite: Objects/Tools/lighters.rsi
heldPrefix: off heldPrefix: off
- type: ItemCooldown - type: ItemCooldown
- type: ItemStatus
- type: RefillableSolution - type: RefillableSolution
solution: Welder solution: Welder
- type: SolutionContainerManager - type: SolutionContainerManager

View File

@@ -33,7 +33,6 @@
damage: damage:
types: types:
Blunt: 5 #i mean... i GUESS you could use it like that Blunt: 5 #i mean... i GUESS you could use it like that
- type: ItemStatus
- type: RefillableSolution - type: RefillableSolution
solution: Welder solution: Welder
- type: SolutionContainerManager - type: SolutionContainerManager

View File

@@ -34,7 +34,6 @@
size: 50 size: 50
sprite: Objects/Weapons/Melee/chainsaw.rsi sprite: Objects/Weapons/Melee/chainsaw.rsi
- type: DisarmMalus - type: DisarmMalus
- type: ItemStatus
- type: RefillableSolution - type: RefillableSolution
solution: Welder solution: Welder
- type: SolutionContainerManager - type: SolutionContainerManager

View File

@@ -97,6 +97,8 @@
- TRayScanner - TRayScanner
- GasAnalyzer - GasAnalyzer
- UtilityBelt - UtilityBelt
- Fulton
- FultonBeacon
- Pickaxe - Pickaxe
- ModularReceiver - ModularReceiver
- AppraisalTool - AppraisalTool

View File

@@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

View File

@@ -0,0 +1,35 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "https://github.com/tgstation/tgstation/blob/a7a67c81afc99d77483c84878173822ee8cbeecd/icons/obj/fulton.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "folded_extraction"
},
{
"name": "extraction_point",
"delays": [
[
1,
0.1
]
]
},
{
"name": "extraction_point_light",
"delays": [
[
1,
0.1
]
]
},
{
"name": "extraction_pack"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -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
]
]
}
]
}