diff --git a/Content.Client/Shuttles/FtlArrivalOverlay.cs b/Content.Client/Shuttles/FtlArrivalOverlay.cs new file mode 100644 index 00000000000..f24a1e96488 --- /dev/null +++ b/Content.Client/Shuttles/FtlArrivalOverlay.cs @@ -0,0 +1,82 @@ +using System.Numerics; +using Content.Shared.Shuttles.Components; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Shared.Enums; +using Robust.Shared.Map.Components; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Client.Shuttles; + +/// +/// Plays a visualization whenever a shuttle is arriving from FTL. +/// +public sealed class FtlArrivalOverlay : Overlay +{ + public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities; + + private EntityLookupSystem _lookups; + private SharedMapSystem _maps; + private SharedTransformSystem _transforms; + private SpriteSystem _sprites; + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IPrototypeManager _protos = default!; + + private readonly HashSet> _visualizers = new(); + + private ShaderInstance _shader; + + public FtlArrivalOverlay() + { + IoCManager.InjectDependencies(this); + _lookups = _entManager.System(); + _transforms = _entManager.System(); + _maps = _entManager.System(); + _sprites = _entManager.System(); + + _shader = _protos.Index("unshaded").Instance(); + } + + protected override bool BeforeDraw(in OverlayDrawArgs args) + { + _visualizers.Clear(); + _lookups.GetEntitiesOnMap(args.MapId, _visualizers); + + return _visualizers.Count > 0; + } + + protected override void Draw(in OverlayDrawArgs args) + { + args.WorldHandle.UseShader(_shader); + + foreach (var (uid, comp) in _visualizers) + { + var grid = comp.Grid; + + if (!_entManager.TryGetComponent(grid, out MapGridComponent? mapGrid)) + continue; + + var texture = _sprites.GetFrame(comp.Sprite, TimeSpan.FromSeconds(comp.Elapsed), loop: false); + comp.Elapsed += (float) _timing.FrameTime.TotalSeconds; + + // Need to manually transform the viewport in terms of the visualizer entity as the grid isn't in position. + var (_, _, worldMatrix, invMatrix) = _transforms.GetWorldPositionRotationMatrixWithInv(uid); + args.WorldHandle.SetTransform(worldMatrix); + var localAABB = invMatrix.TransformBox(args.WorldBounds); + + var tilesEnumerator = _maps.GetLocalTilesEnumerator(grid, mapGrid, localAABB); + + while (tilesEnumerator.MoveNext(out var tile)) + { + var bounds = _lookups.GetLocalBounds(tile, mapGrid.TileSize); + + args.WorldHandle.DrawTextureRect(texture, bounds); + } + } + + args.WorldHandle.UseShader(null); + args.WorldHandle.SetTransform(Matrix3x2.Identity); + } +} diff --git a/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs b/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs index d5154a87bef..73c11de2795 100644 --- a/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs +++ b/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs @@ -38,9 +38,8 @@ public bool EnableShuttlePosition private bool _enableShuttlePosition; private EmergencyShuttleOverlay? _overlay; - public override void Initialize() + private void InitializeEmergency() { - base.Initialize(); SubscribeNetworkEvent(OnShuttlePosMessage); } diff --git a/Content.Client/Shuttles/Systems/ShuttleSystem.cs b/Content.Client/Shuttles/Systems/ShuttleSystem.cs new file mode 100644 index 00000000000..a2c048ff90e --- /dev/null +++ b/Content.Client/Shuttles/Systems/ShuttleSystem.cs @@ -0,0 +1,21 @@ +using Robust.Client.Graphics; + +namespace Content.Client.Shuttles.Systems; + +public sealed partial class ShuttleSystem +{ + [Dependency] private readonly IOverlayManager _overlays = default!; + + public override void Initialize() + { + base.Initialize(); + InitializeEmergency(); + _overlays.AddOverlay(new FtlArrivalOverlay()); + } + + public override void Shutdown() + { + base.Shutdown(); + _overlays.RemoveOverlay(); + } +} diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index 11cc16e0cd0..daf51038c14 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -81,6 +81,8 @@ public sealed partial class ShuttleSystem private void InitializeFTL() { SubscribeLocalEvent(OnStationPostInit); + SubscribeLocalEvent(OnFtlShutdown); + _bodyQuery = GetEntityQuery(); _buckleQuery = GetEntityQuery(); _beaconQuery = GetEntityQuery(); @@ -97,6 +99,12 @@ private void InitializeFTL() _cfg.OnValueChanged(CCVars.HyperspaceKnockdownTime, time => _hyperspaceKnockdownTime = TimeSpan.FromSeconds(time), true); } + private void OnFtlShutdown(Entity ent, ref ComponentShutdown args) + { + Del(ent.Comp.VisualizerEntity); + ent.Comp.VisualizerEntity = null; + } + private void OnStationPostInit(ref StationPostInitEvent ev) { // Add all grid maps as ftl destinations that anyone can FTL to. @@ -422,8 +430,16 @@ private void UpdateFTLTravelling(Entity entity) var comp = entity.Comp1; comp.StateTime = StartEndTime.FromCurTime(_gameTiming, DefaultArrivalTime); comp.State = FTLState.Arriving; - // TODO: Arrival effects - // For now we'll just use the ss13 bubbles but we can do fancier. + + if (entity.Comp1.VisualizerProto != null) + { + comp.VisualizerEntity = SpawnAtPosition(entity.Comp1.VisualizerProto, entity.Comp1.TargetCoordinates); + var visuals = Comp(comp.VisualizerEntity.Value); + visuals.Grid = entity.Owner; + Dirty(comp.VisualizerEntity.Value, visuals); + _transform.SetLocalRotation(comp.VisualizerEntity.Value, entity.Comp1.TargetAngle); + _pvs.AddGlobalOverride(comp.VisualizerEntity.Value); + } _thruster.DisableLinearThrusters(shuttle); _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.South); diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.cs index 6fe2324d51e..a10d0d56144 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.Throwing; using JetBrains.Annotations; using Robust.Server.GameObjects; +using Robust.Server.GameStates; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; @@ -40,6 +41,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly MapLoaderSystem _loader = default!; [Dependency] private readonly MetaDataSystem _metadata = default!; + [Dependency] private readonly PvsOverrideSystem _pvs = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; diff --git a/Content.Server/Shuttles/Components/FTLComponent.cs b/Content.Shared/Shuttles/Components/FTLComponent.cs similarity index 81% rename from Content.Server/Shuttles/Components/FTLComponent.cs rename to Content.Shared/Shuttles/Components/FTLComponent.cs index c9b84064234..9acca7c1d45 100644 --- a/Content.Server/Shuttles/Components/FTLComponent.cs +++ b/Content.Shared/Shuttles/Components/FTLComponent.cs @@ -2,16 +2,16 @@ using Content.Shared.Tag; using Content.Shared.Timing; using Robust.Shared.Audio; +using Robust.Shared.GameStates; using Robust.Shared.Map; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Server.Shuttles.Components; +namespace Content.Shared.Shuttles.Components; /// /// Added to a component when it is queued or is travelling via FTL. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class FTLComponent : Component { // TODO Full game save / add datafields @@ -29,13 +29,19 @@ public sealed partial class FTLComponent : Component [ViewVariables(VVAccess.ReadWrite)] public float TravelTime = 0f; + [DataField] + public EntProtoId? VisualizerProto = "FtlVisualizerEntity"; + + [DataField, AutoNetworkedField] + public EntityUid? VisualizerEntity; + /// /// Coordinates to arrive it: May be relative to another grid (for docking) or map coordinates. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField, AutoNetworkedField] public EntityCoordinates TargetCoordinates; - [DataField] + [DataField, AutoNetworkedField] public Angle TargetAngle; /// diff --git a/Content.Shared/Shuttles/Components/FtlVisualizerComponent.cs b/Content.Shared/Shuttles/Components/FtlVisualizerComponent.cs new file mode 100644 index 00000000000..628a4f828b2 --- /dev/null +++ b/Content.Shared/Shuttles/Components/FtlVisualizerComponent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Utility; + +namespace Content.Shared.Shuttles.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class FtlVisualizerComponent : Component +{ + /// + /// Clientside time tracker for the animation. + /// + [ViewVariables(VVAccess.ReadWrite)] + public float Elapsed; + + [DataField(required: true)] + public SpriteSpecifier.Rsi Sprite; + + /// + /// Target grid to pull FTL visualization from. + /// + [DataField, AutoNetworkedField] + public EntityUid Grid; +} diff --git a/Resources/Prototypes/Entities/Effects/shuttle.yml b/Resources/Prototypes/Entities/Effects/shuttle.yml new file mode 100644 index 00000000000..d4538116ac9 --- /dev/null +++ b/Resources/Prototypes/Entities/Effects/shuttle.yml @@ -0,0 +1,12 @@ +- type: entity + id: FtlVisualizerEntity + noSpawn: true + description: Visualizer for shuttles arriving. You shouldn't see this! + components: + - type: FtlVisualizer + sprite: + sprite: /Textures/Effects/medi_holo.rsi + state: medi_holo + - type: Tag + tags: + - HideContextMenu diff --git a/Resources/Textures/Effects/medi_holo.rsi/medi_holo.png b/Resources/Textures/Effects/medi_holo.rsi/medi_holo.png new file mode 100644 index 00000000000..9b024faa2d7 Binary files /dev/null and b/Resources/Textures/Effects/medi_holo.rsi/medi_holo.png differ diff --git a/Resources/Textures/Effects/medi_holo.rsi/meta.json b/Resources/Textures/Effects/medi_holo.rsi/meta.json new file mode 100644 index 00000000000..1be502223e5 --- /dev/null +++ b/Resources/Textures/Effects/medi_holo.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/tgstation/tgstation/tree/217b39cc85e45302d407d5c1ab60809bd9e18987", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "medi_holo", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} \ No newline at end of file