From 3e8a7d9b00e19e160321eb81d69a884189dfa4e6 Mon Sep 17 00:00:00 2001 From: sleepyyapril <123355664+sleepyyapril@users.noreply.github.com> Date: Tue, 14 Jan 2025 02:08:49 -0400 Subject: [PATCH 01/32] Station AI Features and Fixes (Also General Fixes) (#1525) # Description Check the changelog for the full list. --- # Changelog :cl: - add: Added Holopads (unmapped) - add: Intellicards are now useful for removing/adding a Station AI's brain. - add: Added the Communications Console to Station AI actions. - add: AI now has a warp point. - add: Added more things for the AI to press. - add: More AI laws have been added. - fix: Fixed the mail system - fix: Fixed AI actions - fix: Fixed invalid spawns for station AI breaking and ruining your ability to play it. - fix: The Station AI's name will now properly send in "arrived to the station" announcements. - fix: Changed the CPR sound to simply not loop until fixed. - fix: Fixed unlocalized messages being sent for the random sentience event. --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Co-authored-by: Zachary Higgs Co-authored-by: MendaxxDev <153332064+MendaxxDev@users.noreply.github.com> Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Content.Client/Chat/UI/SpeechBubble.cs | 14 +- .../DeltaV/Hologram/HologramSystem.cs | 2 +- .../Holopad/HolopadBoundUserInterface.cs | 101 ++ Content.Client/Holopad/HolopadSystem.cs | 172 +++ Content.Client/Holopad/HolopadWindow.xaml | 107 ++ Content.Client/Holopad/HolopadWindow.xaml.cs | 338 +++++ Content.Client/Stylesheets/StyleNano.cs | 15 + Content.Client/Telephone/TelephoneSystem.cs | 8 + .../Storage/Controls/StorageContainer.cs | 3 + .../Tests/PostMapInitTest.cs | 14 +- Content.Server/Chat/Systems/ChatSystem.cs | 4 +- .../Clothing/Systems/LoadoutSystem.cs | 5 +- .../GameTicking/GameTicker.Spawning.cs | 1 + Content.Server/Holopad/HolopadSystem.cs | 786 ++++++++++++ Content.Server/Mail/MailCommands.cs | 51 +- Content.Server/Mail/Systems/MailSystem.cs | 1141 +++++++++-------- Content.Server/Medical/CPR/CPRSystem.cs | 2 +- .../Systems/ShuttleSystem.FasterThanLight.cs | 56 +- .../Silicons/Laws/SiliconLawSystem.cs | 2 + .../Silicons/StationAi/AiVisionWireAction.cs | 4 +- .../Silicons/StationAi/StationAiSystem.cs | 44 + .../Components/EmitSoundOnUIOpenComponent.cs | 12 - Content.Server/Sound/EmitSoundSystem.cs | 6 - .../ContainerSpawnPointSystem.cs | 17 +- .../Events/RandomSentienceRule.cs | 15 +- .../SurveillanceCameraMicrophoneSystem.cs | 4 +- Content.Server/Telephone/TelephoneSystem.cs | 476 +++++++ .../Buckle/SharedBuckleSystem.Buckle.cs | 4 +- Content.Shared/Doors/AirlockWireStatus.cs | 3 +- .../Holopad/HolographicAvatarComponent.cs | 13 + Content.Shared/Holopad/HolopadComponent.cs | 133 ++ .../Holopad/HolopadHologramComponent.cs | 71 + .../Holopad/HolopadUserComponent.cs | 104 ++ Content.Shared/Holopad/SharedHolopadSystem.cs | 43 + .../Components/FTLSmashImmuneComponent.cs | 9 + .../Shuttles/Components/NoFTLComponent.cs | 12 + .../Components/SiliconLawProviderComponent.cs | 9 + .../StationAi/SharedStationAiSystem.Held.cs | 2 +- .../StationAi/SharedStationAiSystem.cs | 99 +- .../StationAi/StationAiCoreComponent.cs | 10 + .../Components/EmitSoundOnUIOpenComponent.cs | 17 + Content.Shared/Sound/SharedEmitSoundSystem.cs | 11 +- Content.Shared/Speech/SpeechComponent.cs | 6 + .../Telephone/SharedTelephoneSystem.cs | 39 + .../Telephone/TelephoneComponent.cs | 219 ++++ .../UserInterface/IntrinsicUIComponent.cs | 12 +- .../UserInterface/IntrinsicUISystem.cs | 27 +- .../Audio/Ambience/Antag/attributions.yml | 4 + .../Antag/silicon_lawboard_antimov.ogg | Bin 0 -> 294307 bytes Resources/Audio/Machines/attributions.yml | 5 + Resources/Audio/Machines/double_ring.ogg | Bin 0 -> 16754 bytes Resources/Audio/Misc/attributions.yml | 5 + Resources/Audio/Misc/cryo_warning.ogg | Bin 0 -> 22347 bytes .../communications-console-component.ftl | 3 +- .../Locale/en-US/deltav/station-laws/laws.ftl | 20 - Resources/Locale/en-US/holopad/holopad.ftl | 151 +++ .../interaction-popup-component.ftl | 22 + Resources/Locale/en-US/station-laws/laws.ftl | 56 + .../Locale/en-US/telephone/telephone.ftl | 8 + Resources/Locale/en-US/wires/wire-names.ftl | 4 + .../Maps/Ruins/DeltaV/biodome_satellite.yml | 1 + Resources/Maps/Ruins/DeltaV/old_ai_sat.yml | 1 + .../DeltaV/DV-atlas-conference-room.yml | 1 + .../Maps/Salvage/DeltaV/DV-atlas-epi.yml | 5 + Resources/Maps/Shuttles/DeltaV/NTES_Box.yml | 1 + Resources/Maps/Shuttles/DeltaV/NTES_Delta.yml | 1 + Resources/Maps/Shuttles/DeltaV/NTES_Lox.yml | 1 + .../Maps/Shuttles/DeltaV/NTES_Propeller.yml | 1 + Resources/Maps/Shuttles/DeltaV/barge.yml | 5 + Resources/Maps/Shuttles/emergency.yml | 1 + Resources/Maps/Shuttles/emergency_meta.yml | 1 + Resources/Prototypes/Actions/station_ai.yml | 68 + .../Catalog/Fills/Lockers/heads.yml | 1 + Resources/Prototypes/DeltaV/shaders.yml | 2 +- Resources/Prototypes/DeltaV/siliconlaws.yml | 115 +- .../Prototypes/Entities/Markers/shuttle.yml | 1 + .../Entities/Mobs/Player/admin_ghost.yml | 26 +- .../Entities/Mobs/Player/observer.yml | 1 + .../Entities/Mobs/Player/silicon.yml | 274 +++- .../Devices/Circuitboards/Machine/holopad.yml | 13 + .../Structures/Doors/Airlocks/highsec.yml | 3 + .../Structures/Doors/Firelocks/firelock.yml | 3 + .../Structures/Doors/Shutter/shutters.yml | 1 + .../Doors/Windoors/base_structurewindoors.yml | 3 + .../Structures/Machines/Computers/arcades.yml | 2 + .../Computers/base_structurecomputers.yml | 5 + .../Machines/Computers/computers.yml | 26 +- .../Structures/Machines/anomaly_equipment.yml | 6 + .../Structures/Machines/gravity_generator.yml | 2 + .../Entities/Structures/Machines/holopad.yml | 177 +++ .../Entities/Structures/Machines/jukebox.yml | 7 + .../Structures/Machines/smartfridge.yml | 1 + .../Structures/Machines/vending_machines.yml | 1 + .../Structures/Piping/Atmospherics/binary.yml | 1 + .../Piping/Atmospherics/portable.yml | 1 + .../Structures/Piping/Atmospherics/unary.yml | 2 + .../Structures/Shuttles/station_anchor.yml | 1 + .../Structures/Wallmounts/air_alarm.yml | 2 +- .../Entities/Structures/Wallmounts/screen.yml | 1 + .../Wallmounts/surveillance_camera.yml | 2 - .../Entities/Structures/Wallmounts/switch.yml | 3 + Resources/Prototypes/Shaders/shaders.yml | 5 + Resources/Prototypes/Wires/layouts.yml | 51 +- Resources/Prototypes/silicon-laws.yml | 356 ++++- Resources/Prototypes/tags.yml | 3 + .../Actions/actions_ai.rsi/job_view.png | Bin 0 -> 508 bytes .../Actions/actions_ai.rsi/meta.json | 3 + .../Misc/job_icons.rsi/StationAi.png | Bin 204 -> 170 bytes .../Interface/Misc/job_icons.rsi/meta.json | 2 +- .../Objects/Devices/ai_card.rsi/base.png | Bin 4432 -> 6812 bytes .../Objects/Devices/ai_card.rsi/empty.png | Bin 4292 -> 6697 bytes .../Objects/Devices/ai_card.rsi/full.png | Bin 5121 -> 7638 bytes Resources/Textures/Shaders/hologram.swsl | 23 + .../Structures/Machines/holopad.rsi/base.png | Bin 0 -> 457 bytes .../Structures/Machines/holopad.rsi/blank.png | Bin 0 -> 83 bytes .../Machines/holopad.rsi/icon_in_call.png | Bin 0 -> 414 bytes .../Machines/holopad.rsi/lights_calling.png | Bin 0 -> 425 bytes .../holopad.rsi/lights_hanging_up.png | Bin 0 -> 432 bytes .../Machines/holopad.rsi/lights_in_call.png | Bin 0 -> 326 bytes .../Machines/holopad.rsi/lights_ringing.png | Bin 0 -> 4429 bytes .../Structures/Machines/holopad.rsi/meta.json | 100 ++ .../Machines/holopad.rsi/panel_open.png | Bin 0 -> 221 bytes .../Machines/holopad.rsi/unpowered.png | Bin 0 -> 441 bytes 123 files changed, 4958 insertions(+), 876 deletions(-) create mode 100644 Content.Client/Holopad/HolopadBoundUserInterface.cs create mode 100644 Content.Client/Holopad/HolopadSystem.cs create mode 100644 Content.Client/Holopad/HolopadWindow.xaml create mode 100644 Content.Client/Holopad/HolopadWindow.xaml.cs create mode 100644 Content.Client/Telephone/TelephoneSystem.cs create mode 100644 Content.Server/Holopad/HolopadSystem.cs delete mode 100644 Content.Server/Sound/Components/EmitSoundOnUIOpenComponent.cs create mode 100644 Content.Server/Telephone/TelephoneSystem.cs create mode 100644 Content.Shared/Holopad/HolographicAvatarComponent.cs create mode 100644 Content.Shared/Holopad/HolopadComponent.cs create mode 100644 Content.Shared/Holopad/HolopadHologramComponent.cs create mode 100644 Content.Shared/Holopad/HolopadUserComponent.cs create mode 100644 Content.Shared/Holopad/SharedHolopadSystem.cs create mode 100644 Content.Shared/Shuttles/Components/FTLSmashImmuneComponent.cs create mode 100644 Content.Shared/Shuttles/Components/NoFTLComponent.cs create mode 100644 Content.Shared/Sound/Components/EmitSoundOnUIOpenComponent.cs create mode 100644 Content.Shared/Telephone/SharedTelephoneSystem.cs create mode 100644 Content.Shared/Telephone/TelephoneComponent.cs rename {Content.Server => Content.Shared}/UserInterface/IntrinsicUIComponent.cs (59%) rename {Content.Server => Content.Shared}/UserInterface/IntrinsicUISystem.cs (65%) create mode 100644 Resources/Audio/Ambience/Antag/silicon_lawboard_antimov.ogg create mode 100644 Resources/Audio/Machines/double_ring.ogg create mode 100644 Resources/Audio/Misc/cryo_warning.ogg create mode 100644 Resources/Locale/en-US/holopad/holopad.ftl create mode 100644 Resources/Locale/en-US/telephone/telephone.ftl create mode 100644 Resources/Prototypes/Actions/station_ai.yml create mode 100644 Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/holopad.yml create mode 100644 Resources/Prototypes/Entities/Structures/Machines/holopad.yml create mode 100644 Resources/Textures/Interface/Actions/actions_ai.rsi/job_view.png create mode 100644 Resources/Textures/Shaders/hologram.swsl create mode 100644 Resources/Textures/Structures/Machines/holopad.rsi/base.png create mode 100644 Resources/Textures/Structures/Machines/holopad.rsi/blank.png create mode 100644 Resources/Textures/Structures/Machines/holopad.rsi/icon_in_call.png create mode 100644 Resources/Textures/Structures/Machines/holopad.rsi/lights_calling.png create mode 100644 Resources/Textures/Structures/Machines/holopad.rsi/lights_hanging_up.png create mode 100644 Resources/Textures/Structures/Machines/holopad.rsi/lights_in_call.png create mode 100644 Resources/Textures/Structures/Machines/holopad.rsi/lights_ringing.png create mode 100644 Resources/Textures/Structures/Machines/holopad.rsi/meta.json create mode 100644 Resources/Textures/Structures/Machines/holopad.rsi/panel_open.png create mode 100644 Resources/Textures/Structures/Machines/holopad.rsi/unpowered.png diff --git a/Content.Client/Chat/UI/SpeechBubble.cs b/Content.Client/Chat/UI/SpeechBubble.cs index 68c937a7885..b0902f7e225 100644 --- a/Content.Client/Chat/UI/SpeechBubble.cs +++ b/Content.Client/Chat/UI/SpeechBubble.cs @@ -2,6 +2,8 @@ using Content.Client.Chat.Managers; using Content.Shared.CCVar; using Content.Shared.Chat; +using Content.Shared.Speech; +using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; @@ -17,6 +19,8 @@ public abstract class SpeechBubble : Control [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] protected readonly IConfigurationManager ConfigManager = default!; + private readonly SharedTransformSystem _transformSystem; + public enum SpeechType : byte { Emote, @@ -83,6 +87,7 @@ public SpeechBubble(ChatMessage message, EntityUid senderEntity, string speechSt { IoCManager.InjectDependencies(this); _senderEntity = senderEntity; + _transformSystem = _entityManager.System(); // Use text clipping so new messages don't overlap old ones being pushed up. RectClipContent = true; @@ -139,8 +144,13 @@ protected override void FrameUpdate(FrameEventArgs args) Modulate = Color.White; } - var offset = (-_eyeManager.CurrentEye.Rotation).ToWorldVec() * -EntityVerticalOffset; - var worldPos = xform.WorldPosition + offset; + var baseOffset = 0f; + + if (_entityManager.TryGetComponent(_senderEntity, out var speech)) + baseOffset = speech.SpeechBubbleOffset; + + var offset = (-_eyeManager.CurrentEye.Rotation).ToWorldVec() * -(EntityVerticalOffset + baseOffset); + var worldPos = _transformSystem.GetWorldPosition(xform) + offset; var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale; var screenPos = lowerCenter - new Vector2(ContentSize.X / 2, ContentSize.Y + _verticalOffsetAchieved); diff --git a/Content.Client/DeltaV/Hologram/HologramSystem.cs b/Content.Client/DeltaV/Hologram/HologramSystem.cs index 212a797fd87..0d4cf098e0a 100644 --- a/Content.Client/DeltaV/Hologram/HologramSystem.cs +++ b/Content.Client/DeltaV/Hologram/HologramSystem.cs @@ -17,7 +17,7 @@ public override void Initialize() { base.Initialize(); - _shader = _protoMan.Index("Hologram").InstanceUnique(); + _shader = _protoMan.Index("HologramDeltaV").InstanceUnique(); SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnStartup); } diff --git a/Content.Client/Holopad/HolopadBoundUserInterface.cs b/Content.Client/Holopad/HolopadBoundUserInterface.cs new file mode 100644 index 00000000000..20b55ea8c76 --- /dev/null +++ b/Content.Client/Holopad/HolopadBoundUserInterface.cs @@ -0,0 +1,101 @@ +using Content.Shared.Holopad; +using Content.Shared.Silicons.StationAi; +using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Shared.Player; +using System.Numerics; + +namespace Content.Client.Holopad; + +public sealed class HolopadBoundUserInterface : BoundUserInterface +{ + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; + [Dependency] private readonly IClyde _displayManager = default!; + + [ViewVariables] + private HolopadWindow? _window; + + public HolopadBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + IoCManager.InjectDependencies(this); + } + + protected override void Open() + { + base.Open(); + + _window = this.CreateWindow(); + _window.Title = Loc.GetString("holopad-window-title", ("title", EntMan.GetComponent(Owner).EntityName)); + + if (this.UiKey is not HolopadUiKey) + { + Close(); + return; + } + + var uiKey = (HolopadUiKey)this.UiKey; + + // AIs will see a different holopad interface to crew when interacting with them in the world + if (uiKey == HolopadUiKey.InteractionWindow && EntMan.HasComponent(_playerManager.LocalEntity)) + uiKey = HolopadUiKey.InteractionWindowForAi; + + _window.SetState(Owner, uiKey); + _window.UpdateState(new Dictionary()); + + // Set message actions + _window.SendHolopadStartNewCallMessageAction += SendHolopadStartNewCallMessage; + _window.SendHolopadAnswerCallMessageAction += SendHolopadAnswerCallMessage; + _window.SendHolopadEndCallMessageAction += SendHolopadEndCallMessage; + _window.SendHolopadStartBroadcastMessageAction += SendHolopadStartBroadcastMessage; + _window.SendHolopadActivateProjectorMessageAction += SendHolopadActivateProjectorMessage; + _window.SendHolopadRequestStationAiMessageAction += SendHolopadRequestStationAiMessage; + + // If this call is addressed to an AI, open the window in the bottom right hand corner of the screen + if (uiKey == HolopadUiKey.AiRequestWindow) + _window.OpenCenteredAt(new Vector2(1f, 1f)); + + // Otherwise offset to the left so the holopad can still be seen + else + _window.OpenCenteredAt(new Vector2(0.3333f, 0.50f)); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + var castState = (HolopadBoundInterfaceState)state; + EntMan.TryGetComponent(Owner, out var xform); + + _window?.UpdateState(castState.Holopads); + } + + public void SendHolopadStartNewCallMessage(NetEntity receiver) + { + SendMessage(new HolopadStartNewCallMessage(receiver)); + } + + public void SendHolopadAnswerCallMessage() + { + SendMessage(new HolopadAnswerCallMessage()); + } + + public void SendHolopadEndCallMessage() + { + SendMessage(new HolopadEndCallMessage()); + } + + public void SendHolopadStartBroadcastMessage() + { + SendMessage(new HolopadStartBroadcastMessage()); + } + + public void SendHolopadActivateProjectorMessage() + { + SendMessage(new HolopadActivateProjectorMessage()); + } + + public void SendHolopadRequestStationAiMessage() + { + SendMessage(new HolopadStationAiRequestMessage()); + } +} diff --git a/Content.Client/Holopad/HolopadSystem.cs b/Content.Client/Holopad/HolopadSystem.cs new file mode 100644 index 00000000000..3bd556f1fc2 --- /dev/null +++ b/Content.Client/Holopad/HolopadSystem.cs @@ -0,0 +1,172 @@ +using Content.Shared.Chat.TypingIndicator; +using Content.Shared.Holopad; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using System.Linq; + +namespace Content.Client.Holopad; + +public sealed class HolopadSystem : SharedHolopadSystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnShaderRender); + SubscribeAllEvent(OnTypingChanged); + + SubscribeNetworkEvent(OnPlayerSpriteStateRequest); + SubscribeNetworkEvent(OnPlayerSpriteStateMessage); + } + + private void OnComponentInit(EntityUid uid, HolopadHologramComponent component, ComponentInit ev) + { + if (!TryComp(uid, out var sprite)) + return; + + UpdateHologramSprite(uid); + } + + private void OnShaderRender(EntityUid uid, HolopadHologramComponent component, BeforePostShaderRenderEvent ev) + { + if (ev.Sprite.PostShader == null) + return; + + ev.Sprite.PostShader.SetParameter("t", (float)_timing.CurTime.TotalSeconds * component.ScrollRate); + } + + private void OnTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs args) + { + var uid = args.SenderSession.AttachedEntity; + + if (!Exists(uid)) + return; + + if (!HasComp(uid)) + return; + + var netEv = new HolopadUserTypingChangedEvent(GetNetEntity(uid.Value), ev.IsTyping); + RaiseNetworkEvent(netEv); + } + + private void OnPlayerSpriteStateRequest(PlayerSpriteStateRequest ev) + { + var targetPlayer = GetEntity(ev.TargetPlayer); + var player = _playerManager.LocalSession?.AttachedEntity; + + // Ignore the request if received by a player who isn't the target + if (targetPlayer != player) + return; + + if (!TryComp(player, out var playerSprite)) + return; + + var spriteLayerData = new List(); + + if (playerSprite.Visible) + { + // Record the RSI paths, state names and shader paramaters of all visible layers + for (int i = 0; i < playerSprite.AllLayers.Count(); i++) + { + if (!playerSprite.TryGetLayer(i, out var layer)) + continue; + + if (!layer.Visible || + string.IsNullOrEmpty(layer.ActualRsi?.Path.ToString()) || + string.IsNullOrEmpty(layer.State.Name)) + continue; + + var layerDatum = new PrototypeLayerData(); + layerDatum.RsiPath = layer.ActualRsi.Path.ToString(); + layerDatum.State = layer.State.Name; + + if (layer.CopyToShaderParameters != null) + { + var key = (string)layer.CopyToShaderParameters.LayerKey; + + if (playerSprite.LayerMapTryGet(key, out var otherLayerIdx) && + playerSprite.TryGetLayer(otherLayerIdx, out var otherLayer) && + otherLayer.Visible) + { + layerDatum.MapKeys = new() { key }; + + layerDatum.CopyToShaderParameters = new PrototypeCopyToShaderParameters() + { + LayerKey = key, + ParameterTexture = layer.CopyToShaderParameters.ParameterTexture, + ParameterUV = layer.CopyToShaderParameters.ParameterUV + }; + } + } + + spriteLayerData.Add(layerDatum); + } + } + + // Return the recorded data to the server + var evResponse = new PlayerSpriteStateMessage(ev.TargetPlayer, spriteLayerData.ToArray()); + RaiseNetworkEvent(evResponse); + } + + private void OnPlayerSpriteStateMessage(PlayerSpriteStateMessage ev) + { + UpdateHologramSprite(GetEntity(ev.SpriteEntity), ev.SpriteLayerData); + } + + private void UpdateHologramSprite(EntityUid uid, PrototypeLayerData[]? layerData = null) + { + if (!TryComp(uid, out var hologramSprite)) + return; + + if (!TryComp(uid, out var holopadhologram)) + return; + + for (int i = hologramSprite.AllLayers.Count() - 1; i >= 0; i--) + hologramSprite.RemoveLayer(i); + + if (layerData == null || layerData.Length == 0) + { + layerData = new PrototypeLayerData[1]; + layerData[0] = new PrototypeLayerData() + { + RsiPath = holopadhologram.RsiPath, + State = holopadhologram.RsiState + }; + } + + for (int i = 0; i < layerData.Length; i++) + { + var layer = layerData[i]; + layer.Shader = "unshaded"; + + hologramSprite.AddLayer(layerData[i], i); + } + + UpdateHologramShader(uid, hologramSprite, holopadhologram); + } + + private void UpdateHologramShader(EntityUid uid, SpriteComponent sprite, HolopadHologramComponent holopadHologram) + { + // Find the texture height of the largest layer + float texHeight = sprite.AllLayers.Max(x => x.PixelSize.Y); + + var instance = _prototypeManager.Index(holopadHologram.ShaderName).InstanceUnique(); + instance.SetParameter("color1", new Vector3(holopadHologram.Color1.R, holopadHologram.Color1.G, holopadHologram.Color1.B)); + instance.SetParameter("color2", new Vector3(holopadHologram.Color2.R, holopadHologram.Color2.G, holopadHologram.Color2.B)); + instance.SetParameter("alpha", holopadHologram.Alpha); + instance.SetParameter("intensity", holopadHologram.Intensity); + instance.SetParameter("texHeight", texHeight); + instance.SetParameter("t", (float)_timing.CurTime.TotalSeconds * holopadHologram.ScrollRate); + + sprite.PostShader = instance; + sprite.RaiseShaderEvent = true; + } +} diff --git a/Content.Client/Holopad/HolopadWindow.xaml b/Content.Client/Holopad/HolopadWindow.xaml new file mode 100644 index 00000000000..9c3dfab1ea6 --- /dev/null +++ b/Content.Client/Holopad/HolopadWindow.xaml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +