diff --git a/Content.Client/Paper/UI/PaperBoundUserInterface.cs b/Content.Client/Paper/UI/PaperBoundUserInterface.cs index b6804c1238..1088aee02b 100644 --- a/Content.Client/Paper/UI/PaperBoundUserInterface.cs +++ b/Content.Client/Paper/UI/PaperBoundUserInterface.cs @@ -19,14 +19,18 @@ namespace Content.Client.Paper.UI protected override void Open() { base.Open(); - _window = new PaperWindow - { - Title = IoCManager.Resolve().GetComponent(Owner.Owner).EntityName, - }; + var entityMgr = IoCManager.Resolve(); + + _window = new PaperWindow(); _window.OnClose += Close; _window.Input.OnTextEntered += Input_OnTextEntered; - _window.OpenCentered(); + if (entityMgr.TryGetComponent(Owner.Owner, out var visuals)) + { + _window.InitVisuals(visuals); + } + + _window.OpenCentered(); } protected override void UpdateState(BoundUserInterfaceState state) diff --git a/Content.Client/Paper/UI/PaperVisualsComponent.cs b/Content.Client/Paper/UI/PaperVisualsComponent.cs index 38c987e7a6..fb6126b77e 100644 --- a/Content.Client/Paper/UI/PaperVisualsComponent.cs +++ b/Content.Client/Paper/UI/PaperVisualsComponent.cs @@ -1,6 +1,99 @@ -namespace Content.Client.Paper; +namespace Content.Client.Paper; [RegisterComponent] public sealed class PaperVisualsComponent : Component { + /// + /// The path to the image which will be used as a background for the paper itself + /// + [DataField("backgroundImagePath")] + public string? BackgroundImagePath; + + /// + /// An optional patch to configure tiling stretching of the background. Used to set + /// the PatchMargin in a StyleBoxTexture + /// + [DataField("backgroundPatchMargin")] + public Box2 BackgroundPatchMargin = default; + + /// + /// Modulate the background image by this color. Can be used to add colorful + /// variants of images, without having to create new textures. + /// + [DataField("backgroundModulate")] + public Color BackgroundModulate = Color.White; + + /// + /// Should the background image tile, or be streched? Sets StyleBoxTexture.StrechMode + /// + [DataField("backgroundImageTile")] + public bool BackgroundImageTile = false; + + /// + /// An additional scale to apply to the background image + /// + [DataField("backgroundScale")] + public Vector2 BackgroundScale = Vector2.One; + + /// + /// A path to an image which will be used as a header on the paper + /// + [DataField("headerImagePath")] + public string? HeaderImagePath; + + /// + /// Modulate the header image by this color + /// + [DataField("headerImageModulate")] + public Color HeaderImageModulate = Color.White; + + /// + /// Any additional margin to add around the header + /// + [DataField("headerMargin")] + public Box2 HeaderMargin = default; + + /// + /// Path to an image to use as the background to the "content" of the paper + /// The header and actual written text will use this as a background. The + /// image will be tiled vertically with the property that the bottom of the + /// written text will line up with the bottom of this image. + /// + [DataField("contentImagePath")] + public string? ContentImagePath; + + /// + /// Modulate the content image by this color + /// + [DataField("contentImageModulate")] + public Color ContentImageModulate = Color.White; + + /// + /// An additional margin around the content (including header) + /// + [DataField("contentMargin")] + public Box2 ContentMargin = default; + + /// + /// The number of lines that the content image represents. The + /// content image will be vertically tiled after this many lines + /// of text. + /// + [DataField("contentImageNumLines")] + public int ContentImageNumLines = 1; + + /// + /// Modulate the style's font by this color + /// + [DataField("fontAccentColor")] + public Color FontAccentColor = new Color(0x25, 0x25, 0x2a); + + /// + /// This can enforce that your paper has a limited area to write in. + /// If you wish to constrain only one direction, the other direction + /// can be unlimited by specifying a value of zero. + /// This will be scaled according to UI scale. + /// + [DataField("maxWritableArea")] + public Vector2? MaxWritableArea = null; } diff --git a/Content.Client/Paper/UI/PaperWindow.xaml b/Content.Client/Paper/UI/PaperWindow.xaml index f1b875c228..60f4e36e00 100644 --- a/Content.Client/Paper/UI/PaperWindow.xaml +++ b/Content.Client/Paper/UI/PaperWindow.xaml @@ -1,10 +1,31 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/Content.Client/Paper/UI/PaperWindow.xaml.cs b/Content.Client/Paper/UI/PaperWindow.xaml.cs index ee99882c20..218a3c2d7e 100644 --- a/Content.Client/Paper/UI/PaperWindow.xaml.cs +++ b/Content.Client/Paper/UI/PaperWindow.xaml.cs @@ -1,6 +1,7 @@ -using Content.Shared.Paper; +using Content.Shared.Paper; using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.Controls; +using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Utility; @@ -8,22 +9,223 @@ using Robust.Shared.Utility; namespace Content.Client.Paper.UI { [GenerateTypedNameReferences] - public sealed partial class PaperWindow : DefaultWindow + public sealed partial class PaperWindow : BaseWindow { + // + // Size of resize handles around the paper + private const int DRAG_MARGIN_SIZE = 16; + + // We keep a reference to the paper content texture that we create + // so that we can modify it later. + private StyleBoxTexture _paperContentTex = new(); + + // The number of lines that the content image represents. + // See PaperVisualsComponent.ContentImageNumLines. + private float _paperContentLineScale = 1.0f; + + // If paper limits the size in one or both axes, it'll affect whether + // we're able to resize this UI or not. Default to everything enabled: + private DragMode _allowedResizeModes = ~DragMode.None; + public PaperWindow() { RobustXamlLoader.Load(this); + + // We can't configure the RichTextLabel contents from xaml, so do it here: + BlankPaperIndicator.SetMessage(Loc.GetString("paper-ui-blank-page-message")); + + // Hook up the close button: + CloseButton.OnPressed += _ => Close(); } + /// + /// Initialize this UI according to visuals Initializes + /// textures, recalculates sizes, and applies some layout rules. + /// + public void InitVisuals(PaperVisualsComponent visuals) + { + var resCache = IoCManager.Resolve(); + + /// Initialize the background: + PaperBackground.ModulateSelfOverride = visuals.BackgroundModulate; + var backgroundImage = visuals.BackgroundImagePath != null? resCache.GetResource(visuals.BackgroundImagePath) : null; + if (backgroundImage != null) + { + var backgroundImageMode = visuals.BackgroundImageTile ? StyleBoxTexture.StretchMode.Tile : StyleBoxTexture.StretchMode.Stretch; + var backgroundPatchMargin = visuals.BackgroundPatchMargin; + PaperBackground.PanelOverride = new StyleBoxTexture + { + Texture = backgroundImage, + TextureScale = visuals.BackgroundScale, + Mode = backgroundImageMode, + PatchMarginLeft = backgroundPatchMargin.Left, + PatchMarginBottom = backgroundPatchMargin.Bottom, + PatchMarginRight = backgroundPatchMargin.Right, + PatchMarginTop = backgroundPatchMargin.Top + }; + + } + else + { + PaperBackground.PanelOverride = null; + } + + + // Then the header: + if (visuals.HeaderImagePath != null) + { + HeaderImage.TexturePath = visuals.HeaderImagePath; + HeaderImage.MinSize = HeaderImage.TextureNormal?.Size ?? Vector2.Zero; + } + + HeaderImage.ModulateSelfOverride = visuals.HeaderImageModulate; + HeaderImage.Margin = new Thickness(visuals.HeaderMargin.Left, visuals.HeaderMargin.Top, + visuals.HeaderMargin.Right, visuals.HeaderMargin.Bottom); + + + PaperContent.ModulateSelfOverride = visuals.ContentImageModulate; + WrittenTextLabel.ModulateSelfOverride = visuals.FontAccentColor; + + var contentImage = visuals.ContentImagePath != null ? resCache.GetResource(visuals.ContentImagePath) : null; + if (contentImage != null) + { + // Setup the paper content texture, but keep a reference to it, as we can't set + // some font-related properties here. We'll fix those up later, in Draw() + _paperContentTex = new StyleBoxTexture + { + Texture = contentImage, + Mode = StyleBoxTexture.StretchMode.Tile, + }; + PaperContent.PanelOverride = _paperContentTex; + _paperContentLineScale = visuals.ContentImageNumLines; + } + + PaperContent.Margin = new Thickness( + visuals.ContentMargin.Left, visuals.ContentMargin.Top, + visuals.ContentMargin.Right, visuals.ContentMargin.Bottom); + + if (visuals.MaxWritableArea != null) + { + var a = (Vector2)visuals.MaxWritableArea; + // Paper has requested that this has a maximum area that you can write on. + // So, we'll make the window non-resizable and fix the size of the content. + // Ideally, would like to be able to allow resizing only one direction. + ScrollingContents.MinSize = Vector2.Zero; + ScrollingContents.MinSize = (Vector2)(a); + + if (a.X > 0.0f) + { + ScrollingContents.MaxWidth = a.X; + _allowedResizeModes &= ~(DragMode.Left | DragMode.Right); + + // Since this dimension has been specified by the user, we + // need to undo the SetSize which was configured in the xaml. + // Controls use NaNs to indicate unset for this value. + // This is leaky - there should be a method for this + SetWidth = float.NaN; + } + + if (a.Y > 0.0f) + { + ScrollingContents.MaxHeight = a.Y; + _allowedResizeModes &= ~(DragMode.Top | DragMode.Bottom); + SetHeight = float.NaN; + } + } + } + + /// + /// Control interface. We'll mostly rely on the children to do the drawing + /// but in order to get lines on paper to match up with the rich text labels, + /// we need to do a small calculation to sync them up. + /// + protected override void Draw(DrawingHandleScreen handle) + { + // Now do the deferred setup of the written area. At the point + // that InitVisuals runs, the label hasn't had it's style initialized + // so we need to get some info out now: + if (WrittenTextLabel.TryGetStyleProperty("font", out var font)) + { + float fontLineHeight = font.GetLineHeight(UIScale); + // This positions the texture so the font baseline is on the bottom: + _paperContentTex.ExpandMarginTop = font.GetDescent(UIScale); + // And this scales the texture so that it's a single text line: + var scaleY = (_paperContentLineScale * fontLineHeight) / _paperContentTex.Texture?.Height ?? fontLineHeight; + _paperContentTex.TextureScale = new Vector2(1, scaleY); + + // Now, we might need to add some padding to the text to ensure + // that, even if a header is specified, the text will line up with + // where the content image expects the font to be rendered (i.e., + // adjusting the height of the header image shouldn't cause the + // text to be offset from a line) + { + var headerHeight = HeaderImage.Size.Y + HeaderImage.Margin.Top + HeaderImage.Margin.Bottom; + var headerInLines = headerHeight / (fontLineHeight * _paperContentLineScale); + var paddingRequiredInLines = (float)Math.Ceiling(headerInLines) - headerInLines; + var verticalMargin = fontLineHeight * paddingRequiredInLines * _paperContentLineScale; + TextAlignmentPadding.Margin = new Thickness(0.0f, verticalMargin, 0.0f, 0.0f); + } + } + + base.Draw(handle); + } + + /// + /// Initialize the paper contents, i.e. the text typed by the + /// user and any stamps that have peen put on the page. + /// public void Populate(SharedPaperComponent.PaperBoundUserInterfaceState state) { - if (state.Mode == SharedPaperComponent.PaperAction.Write) - { - Input.Visible = true; - } + bool isEditing = state.Mode == SharedPaperComponent.PaperAction.Write; + InputContainer.Visible = isEditing; + var msg = new FormattedMessage(); - msg.AddMarkupPermissive(state.Text); - Label.SetMessage(msg); + // Remove any newlines from the end of the message. There can be a trailing + // new line at the end of user input, and we would like to display the input + // box immediately on the next line. + msg.AddMarkupPermissive(state.Text.TrimEnd('\r', '\n')); + WrittenTextLabel.SetMessage(msg); + WrittenTextLabel.Visible = state.Text.Length > 0; + + BlankPaperIndicator.Visible = !isEditing && state.Text.Length == 0; + + StampDisplay.RemoveAllChildren(); + foreach(var stamper in state.StampedBy) + { + StampDisplay.AddChild(new StampWidget{ Stamper = stamper }); + } + } + + /// + /// BaseWindow interface. Allow users to drag UI around by grabbing + /// anywhere on the page (like FancyWindow) but try to calculate + /// reasonable dragging bounds because this UI can have round corners, + /// and it can be hard to judge where to click to resize. + /// + protected override DragMode GetDragModeFor(Vector2 relativeMousePos) + { + var mode = DragMode.Move; + + // Be quite generous with resize margins: + if (relativeMousePos.Y < DRAG_MARGIN_SIZE) + { + mode |= DragMode.Top; + } + else if (relativeMousePos.Y > Size.Y - DRAG_MARGIN_SIZE) + { + mode |= DragMode.Bottom; + } + + if (relativeMousePos.X < DRAG_MARGIN_SIZE) + { + mode |= DragMode.Left; + } + else if (relativeMousePos.X > Size.X - DRAG_MARGIN_SIZE) + { + mode |= DragMode.Right; + } + + return mode & _allowedResizeModes; } } } diff --git a/Content.Client/Paper/UI/StampWidget.xaml b/Content.Client/Paper/UI/StampWidget.xaml new file mode 100644 index 0000000000..5aeb9788fd --- /dev/null +++ b/Content.Client/Paper/UI/StampWidget.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/Content.Client/Paper/UI/StampWidget.xaml.cs b/Content.Client/Paper/UI/StampWidget.xaml.cs new file mode 100644 index 0000000000..a6c6b3232c --- /dev/null +++ b/Content.Client/Paper/UI/StampWidget.xaml.cs @@ -0,0 +1,23 @@ +using Content.Shared.Paper; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Utility; + +namespace Content.Client.Paper.UI +{ + [GenerateTypedNameReferences] + public sealed partial class StampWidget : Container + { + public string? Stamper { + get => StampedByLabel.Text; + set => StampedByLabel.Text = value; + } + + public StampWidget() + { + RobustXamlLoader.Load(this); + } + } +} diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs index cb937b0c5c..b329f0d550 100644 --- a/Content.Client/Stylesheets/StyleNano.cs +++ b/Content.Client/Stylesheets/StyleNano.cs @@ -485,6 +485,14 @@ namespace Content.Client.Stylesheets }; insetBack.SetPatchMargin(StyleBox.Margin.All, 10); + // Default paper background: + var paperBackground = new StyleBoxTexture + { + Texture = resCache.GetTexture("/Textures/Interface/Paper/paper_background_default.svg.96dpi.png"), + Modulate = Color.FromHex("#eaedde"), // A light cream + }; + paperBackground.SetPatchMargin(StyleBox.Margin.All, 16.0f); + var contextMenuExpansionTexture = resCache.GetTexture("/Textures/Interface/VerbIcons/group.svg.192dpi.png"); var verbMenuConfirmationTexture = resCache.GetTexture("/Textures/Interface/VerbIcons/group.svg.192dpi.png"); @@ -1323,6 +1331,16 @@ namespace Content.Client.Stylesheets .Prop(Control.StylePropertyModulateSelf, Color.FromHex("#753131")), // --- + // The default look of paper in UIs. Pages can have components which override this + Element().Class("PaperDefaultBorder") + .Prop(PanelContainer.StylePropertyPanel, paperBackground), + Element().Class("PaperWrittenText") + .Prop(Label.StylePropertyFont, notoSans12) + .Prop(Control.StylePropertyModulateSelf, Color.FromHex("#111111")), + + Element().Class("PaperLineEdit") + .Prop(LineEdit.StylePropertyStyleBox, new StyleBoxEmpty()), + // Red Button --- Element [DataField("printerOutput", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string PrinterOutput = "Paper"; + public string PrinterOutput = "PaperCargoInvoice"; [DataField("receiverPort", customTypeSerializer: typeof(PrototypeIdSerializer))] public string ReceiverPort = "OrderReceiver"; diff --git a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs index 8e1e6d89b8..e29ef550d1 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs @@ -465,7 +465,7 @@ public sealed partial class CargoSystem return; // spawn a piece of paper. - var printed = EntityManager.SpawnEntity("Paper", coordinates); + var printed = EntityManager.SpawnEntity(component.PrinterOutput, coordinates); if (!TryComp(printed, out var paper)) return; diff --git a/Content.Server/Fax/FaxConstants.cs b/Content.Server/Fax/FaxConstants.cs index 6ca12c4e52..102510bd46 100644 --- a/Content.Server/Fax/FaxConstants.cs +++ b/Content.Server/Fax/FaxConstants.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Fax; +namespace Content.Server.Fax; public static class FaxConstants { @@ -23,6 +23,7 @@ public static class FaxConstants public const string FaxNameData = "fax_data_name"; public const string FaxPaperNameData = "fax_data_title"; + public const string FaxPaperPrototypeData = "fax_data_prototype"; public const string FaxPaperContentData = "fax_data_content"; public const string FaxPaperStampStateData = "fax_data_stamp_state"; public const string FaxPaperStampedByData = "fax_data_stamped_by"; diff --git a/Content.Server/Fax/FaxMachineComponent.cs b/Content.Server/Fax/FaxMachineComponent.cs index 1e7b8adc1a..07402aefc8 100644 --- a/Content.Server/Fax/FaxMachineComponent.cs +++ b/Content.Server/Fax/FaxMachineComponent.cs @@ -1,5 +1,7 @@ using Content.Shared.Containers.ItemSlots; using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Fax; @@ -33,21 +35,21 @@ public sealed class FaxMachineComponent : Component [ViewVariables(VVAccess.ReadWrite)] [DataField("responsePings")] public bool ResponsePings { get; set; } = true; - + /// /// Should admins be notified on message receive /// [ViewVariables(VVAccess.ReadWrite)] [DataField("notifyAdmins")] public bool NotifyAdmins { get; set; } = false; - + /// /// Should that fax receive nuke codes send by admins. Probably should be captain fax only /// [ViewVariables(VVAccess.ReadWrite)] [DataField("receiveNukeCodes")] public bool ReceiveNukeCodes { get; set; } = false; - + /// /// Is fax was emaaged /// @@ -134,16 +136,20 @@ public sealed class FaxPrintout [DataField("content")] public string Content { get; } + [DataField("prototypeId", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string PrototypeId { get; } + [DataField("stampState")] public string? StampState { get; } [DataField("stampedBy")] public List StampedBy { get; } - public FaxPrintout(string content, string name, string? stampState = null, List? stampedBy = null) + public FaxPrintout(string content, string name, string? prototypeId, string? stampState = null, List? stampedBy = null) { Content = content; Name = name; + PrototypeId = prototypeId ?? ""; StampState = stampState; StampedBy = stampedBy ?? new List(); } diff --git a/Content.Server/Fax/FaxSystem.cs b/Content.Server/Fax/FaxSystem.cs index 362941286f..810ff7890d 100644 --- a/Content.Server/Fax/FaxSystem.cs +++ b/Content.Server/Fax/FaxSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Administration; +using Content.Server.Administration; using Content.Server.Administration.Managers; using Content.Server.Chat.Managers; using Content.Server.DeviceNetwork; @@ -275,8 +275,9 @@ public sealed class FaxSystem : EntitySystem args.Data.TryGetValue(FaxConstants.FaxPaperStampStateData, out string? stampState); args.Data.TryGetValue(FaxConstants.FaxPaperStampedByData, out List? stampedBy); + args.Data.TryGetValue(FaxConstants.FaxPaperPrototypeData, out string? prototypeId); - var printout = new FaxPrintout(content, name, stampState, stampedBy); + var printout = new FaxPrintout(content, name, prototypeId, stampState, stampedBy); Receive(uid, printout, args.SenderAddress); break; @@ -397,12 +398,21 @@ public sealed class FaxSystem : EntitySystem { FaxConstants.FaxPaperContentData, paper.Content }, }; + if (metadata.EntityPrototype != null) + { + /// todo: Ideally, we could just make a copy of the whole entity when it's + /// faxed, in order to preserve visuals, etc.. This functionality isn't + /// available yet, so we'll pass along the originating prototypeId and fall + /// back to "Paper" in SpawnPaperFromQueue if we can't find one here. + payload[FaxConstants.FaxPaperPrototypeData] = metadata.EntityPrototype.ID; + } + if (paper.StampState != null) { payload[FaxConstants.FaxPaperStampStateData] = paper.StampState; payload[FaxConstants.FaxPaperStampedByData] = paper.StampedBy; } - + _deviceNetworkSystem.QueuePacket(uid, component.DestinationFaxAddress, payload); _adminLogger.Add(LogType.Action, LogImpact.Low, $"{(sender != null ? ToPrettyString(sender.Value) : "Unknown"):user} sent fax from \"{component.FaxName}\" {ToPrettyString(uid)} to {faxName} ({component.DestinationFaxAddress}): {paper.Content}"); @@ -442,7 +452,9 @@ public sealed class FaxSystem : EntitySystem return; var printout = component.PrintingQueue.Dequeue(); - var printed = EntityManager.SpawnEntity("Paper", Transform(uid).Coordinates); + + var entityToSpawn = printout.PrototypeId.Length == 0 ? "Paper" : printout.PrototypeId; + var printed = EntityManager.SpawnEntity(entityToSpawn, Transform(uid).Coordinates); if (TryComp(printed, out var paper)) { diff --git a/Content.Server/Nuke/NukeCodePaperSystem.cs b/Content.Server/Nuke/NukeCodePaperSystem.cs index d8546170da..28467e1e3a 100644 --- a/Content.Server/Nuke/NukeCodePaperSystem.cs +++ b/Content.Server/Nuke/NukeCodePaperSystem.cs @@ -57,6 +57,7 @@ namespace Content.Server.Nuke var printout = new FaxPrintout( paperContent, Loc.GetString("nuke-codes-fax-paper-name"), + null, "paper_stamp-cent", new() { Loc.GetString("stamp-component-stamped-name-centcom") }); _faxSystem.Receive(fax.Owner, printout, null, fax); @@ -84,9 +85,9 @@ namespace Content.Server.Nuke { return false; } - + var owningStation = station ?? _station.GetOwningStation(uid); - + // Find the first nuke that matches the passed location. foreach (var nuke in EntityQuery()) { diff --git a/Content.Server/Paper/PaperSystem.cs b/Content.Server/Paper/PaperSystem.cs index 11213ffc38..c763a51d6f 100644 --- a/Content.Server/Paper/PaperSystem.cs +++ b/Content.Server/Paper/PaperSystem.cs @@ -166,7 +166,7 @@ namespace Content.Server.Paper if (!Resolve(uid, ref paperComp)) return; - _uiSystem.GetUiOrNull(uid, PaperUiKey.Key)?.SetState(new PaperBoundUserInterfaceState(paperComp.Content, paperComp.Mode)); + _uiSystem.GetUiOrNull(uid, PaperUiKey.Key)?.SetState(new PaperBoundUserInterfaceState(paperComp.Content, paperComp.StampedBy, paperComp.Mode)); } } } diff --git a/Content.Shared/Cargo/Components/CargoShuttleComponent.cs b/Content.Shared/Cargo/Components/CargoShuttleComponent.cs index 5e85babab0..d3c5b693cd 100644 --- a/Content.Shared/Cargo/Components/CargoShuttleComponent.cs +++ b/Content.Shared/Cargo/Components/CargoShuttleComponent.cs @@ -1,4 +1,6 @@ using Robust.Shared.Map; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Cargo.Components; @@ -28,4 +30,11 @@ public sealed class CargoShuttleComponent : Component /// [DataField("station")] public EntityUid? Station; + + /// + /// The paper-type prototype to spawn with the order information. + /// + [DataField("printerOutput", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string PrinterOutput = "PaperCargoInvoice"; + } diff --git a/Content.Shared/Paper/SharedPaperComponent.cs b/Content.Shared/Paper/SharedPaperComponent.cs index ecc4a455ef..0518cbd4df 100644 --- a/Content.Shared/Paper/SharedPaperComponent.cs +++ b/Content.Shared/Paper/SharedPaperComponent.cs @@ -8,11 +8,13 @@ namespace Content.Shared.Paper public sealed class PaperBoundUserInterfaceState : BoundUserInterfaceState { public readonly string Text; + public readonly List StampedBy; public readonly PaperAction Mode; - public PaperBoundUserInterfaceState(string text, PaperAction mode = PaperAction.Read) + public PaperBoundUserInterfaceState(string text, List stampedBy, PaperAction mode = PaperAction.Read) { Text = text; + StampedBy = stampedBy; Mode = mode; } } diff --git a/Resources/Locale/en-US/paper/paper-component.ftl b/Resources/Locale/en-US/paper/paper-component.ftl index 61acdc22eb..d10201b4cf 100644 --- a/Resources/Locale/en-US/paper/paper-component.ftl +++ b/Resources/Locale/en-US/paper/paper-component.ftl @@ -1,6 +1,8 @@ ### UI +paper-ui-blank-page-message = This page intentionally left blank + # Shown when paper with words examined details paper-component-examine-detail-has-words = {CAPITALIZE(THE($paper))} has something written on it. # Shown when paper with stamps examined diff --git a/Resources/Prototypes/Entities/Objects/Misc/books.yml b/Resources/Prototypes/Entities/Objects/Misc/books.yml index fce7cf0d47..a567e99ce4 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/books.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/books.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity name: book parent: BaseItem id: BookBase @@ -21,6 +21,10 @@ - type: Tag tags: - Book + - type: PaperVisuals + backgroundImagePath: "/Textures/Interface/Paper/paper_background_book.svg.96dpi.png" + backgroundPatchMargin: 23.0, 16.0, 14.0, 15.0 + contentMargin: 20.0, 20.0, 20.0, 20.0 - type: entity parent: BookBase diff --git a/Resources/Prototypes/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/Entities/Objects/Misc/paper.yml index cbb7ddba32..2e84055151 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/paper.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/paper.yml @@ -32,6 +32,102 @@ - type: PaperVisuals - type: Recyclable +- type: entity + name: office paper + parent: Paper + id: PaperOffice + description: 'A plain sheet of office paper.' + components: + - type: PaperVisuals + backgroundImagePath: "/Textures/Interface/Paper/paper_background_default.svg.96dpi.png" + contentImagePath: "/Textures/Interface/Paper/paper_content_lined.svg.96dpi.png" + backgroundPatchMargin: 16.0, 16.0, 16.0, 16.0 + contentMargin: 16.0, 16.0, 16.0, 16.0 + +- type: entity + name: artifact analyzer printout + parent: Paper + id: PaperArtifactAnalyzer + description: 'The readout of a device forgotten to time' + components: + - type: Sprite + sprite: Objects/Misc/bureaucracy.rsi + netsync: false + layers: + - state: paper_dotmatrix + - state: paper_dotmatrix_words + map: ["enum.PaperVisualLayers.Writing"] + visible: false + - state: paper_stamp-generic + map: ["enum.PaperVisualLayers.Stamp"] + visible: false + - type: PaperVisuals + headerImagePath: "/Textures/Interface/Paper/paper_heading_artifact_analyzer.svg.96dpi.png" + headerMargin: 0.0, 0.0, 0.0, 16.0 + backgroundImagePath: "/Textures/Interface/Paper/paper_background_dotmatrix.svg.96dpi.png" + backgroundImageTile: true + backgroundPatchMargin: 37.0, 0.0, 37.0, 0.0 + contentImagePath: "/Textures/Interface/Paper/paper_content_dotmatrix.svg.96dpi.png" + contentImageNumLines: 2 + contentMargin: 16.0, 16.0, 16.0, 0.0 + # Make this a wide dot-matrix printer + maxWritableArea: 400.0, 0.0 + +- type: entity + name: captain's thoughts + parent: Paper + id: PaperCaptainsThoughts + description: "A page of the captain's journal. In luxurious lavender." + components: + - type: Sprite + sprite: Objects/Misc/bureaucracy.rsi + netsync: false + layers: + - state: paper + color: "#e6e6fa" + - state: paper_words + map: ["enum.PaperVisualLayers.Writing"] + color: "#e6e6fa" + visible: false + - state: paper_stamp-generic + map: ["enum.PaperVisualLayers.Stamp"] + visible: false + - type: PaperVisuals + headerImagePath: "/Textures/Interface/Paper/paper_heading_captains_thoughts.svg.96dpi.png" + backgroundImagePath: "/Textures/Interface/Paper/paper_background_default.svg.96dpi.png" + backgroundModulate: "#e6e6fa" + backgroundPatchMargin: 16.0, 16.0, 16.0, 16.0 + contentMargin: 32.0, 16.0, 32.0, 0.0 + +- type: entity + name: cargo invoice + parent: Paper + id: PaperCargoInvoice + description: 'A single unit of bureaucracy.' + components: + - type: Sprite + sprite: Objects/Misc/bureaucracy.rsi + netsync: false + layers: + - state: paper + color: "#f7e574" + - state: paper_words + map: ["enum.PaperVisualLayers.Writing"] + color: "#f7e574" + visible: false + - state: paper_stamp-generic + map: ["enum.PaperVisualLayers.Stamp"] + visible: false + - type: PaperVisuals + backgroundImagePath: "/Textures/Interface/Paper/paper_background_default.svg.96dpi.png" + contentImagePath: "/Textures/Interface/Paper/paper_content_lined.svg.96dpi.png" + backgroundModulate: "#f7e574" + contentImageModulate: "#f7e574" + backgroundPatchMargin: 16.0, 16.0, 16.0, 16.0 + contentMargin: 16.0, 16.0, 16.0, 16.0 + headerImagePath: "/Textures/Interface/Paper/paper_heading_cargo_invoice.svg.96dpi.png" + headerMargin: 0.0, 12.0, 0.0, 0.0 + - type: entity parent: Paper id: PaperWritten @@ -171,11 +267,11 @@ contents: - id: Paper prob: 0.5 - - id: Paper + - id: PaperOffice prob: 0.4 - id: Paper prob: 0.3 - - id: Paper + - id: PaperOffice prob: 0.2 - id: Paper prob: 0.2 diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 8ca4861ead..f2a3362645 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -370,6 +370,7 @@ state: tech_key - type: ResearchClient - type: AnalysisConsole + reportEntityId: PaperArtifactAnalyzer - type: DeviceList - type: DeviceNetwork deviceNetId: Wired diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml index 5e56846135..958e3f798d 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml @@ -17,10 +17,39 @@ netsync: false - type: DiseaseDiagnoser - type: DiseaseMachine - machineOutput: Paper + machineOutput: DiagnosisReportPaper - type: Appearance - type: DiseaseMachineVisuals idleState: icon runningState: running - type: Machine - board: DiagnoserMachineCircuitboard \ No newline at end of file + board: DiagnoserMachineCircuitboard + +- type: entity + name: disease diagnoser report + parent: Paper + id: DiagnosisReportPaper + description: 'A chilling medical receipt.' + components: + - type: Sprite + sprite: Objects/Misc/bureaucracy.rsi + netsync: false + layers: + - state: paper_receipt + - state: paper_receipt_words + map: ["enum.PaperVisualLayers.Writing"] + visible: false + - state: paper_stamp-generic + map: ["enum.PaperVisualLayers.Stamp"] + visible: false + - type: PaperVisuals + backgroundImagePath: "/Textures/Interface/Paper/paper_background_perforated.svg.96dpi.png" + headerImagePath: "/Textures/Interface/Paper/paper_heading_virus.svg.96dpi.png" + headerMargin: 0.0, 0.0, 0.0, 6.0 + backgroundImageTile: true + backgroundPatchMargin: 0.0, 6.0, 0.0, 6.0 + contentMargin: 12.0, 0.0, 12.0, 0.0 + # This is a narrow piece of paper + maxWritableArea: 128.0, 0.0 + + diff --git a/Resources/Textures/Interface/Nano/lined_paper.svg b/Resources/Textures/Interface/Nano/lined_paper.svg new file mode 100644 index 0000000000..86185ed457 --- /dev/null +++ b/Resources/Textures/Interface/Nano/lined_paper.svg @@ -0,0 +1,77 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/Resources/Textures/Interface/Nano/lined_paper.svg.96dpi.png b/Resources/Textures/Interface/Nano/lined_paper.svg.96dpi.png new file mode 100644 index 0000000000..d57adb5f50 Binary files /dev/null and b/Resources/Textures/Interface/Nano/lined_paper.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_background_book.svg b/Resources/Textures/Interface/Paper/paper_background_book.svg new file mode 100644 index 0000000000..fab991b462 --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_background_book.svg @@ -0,0 +1,143 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_background_book.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_background_book.svg.96dpi.png new file mode 100644 index 0000000000..b0e9c06fe2 Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_background_book.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_background_default.svg b/Resources/Textures/Interface/Paper/paper_background_default.svg new file mode 100644 index 0000000000..fcd323253a --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_background_default.svg @@ -0,0 +1,70 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_background_default.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_background_default.svg.96dpi.png new file mode 100644 index 0000000000..6a4c35bea3 Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_background_default.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_background_dotmatrix.svg b/Resources/Textures/Interface/Paper/paper_background_dotmatrix.svg new file mode 100644 index 0000000000..79ef1d6a7b --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_background_dotmatrix.svg @@ -0,0 +1,214 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_background_dotmatrix.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_background_dotmatrix.svg.96dpi.png new file mode 100644 index 0000000000..c1bee72561 Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_background_dotmatrix.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_background_perforated.svg b/Resources/Textures/Interface/Paper/paper_background_perforated.svg new file mode 100644 index 0000000000..128947fd87 --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_background_perforated.svg @@ -0,0 +1,73 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_background_perforated.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_background_perforated.svg.96dpi.png new file mode 100644 index 0000000000..932a52118b Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_background_perforated.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_content_dotmatrix.svg b/Resources/Textures/Interface/Paper/paper_content_dotmatrix.svg new file mode 100644 index 0000000000..f231334c19 --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_content_dotmatrix.svg @@ -0,0 +1,91 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_content_dotmatrix.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_content_dotmatrix.svg.96dpi.png new file mode 100644 index 0000000000..c1a4f866c9 Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_content_dotmatrix.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_content_lined.svg b/Resources/Textures/Interface/Paper/paper_content_lined.svg new file mode 100644 index 0000000000..891663615d --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_content_lined.svg @@ -0,0 +1,77 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_content_lined.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_content_lined.svg.96dpi.png new file mode 100644 index 0000000000..3ebe3fe088 Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_content_lined.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_heading_artifact_analyzer.svg b/Resources/Textures/Interface/Paper/paper_heading_artifact_analyzer.svg new file mode 100644 index 0000000000..71836e855c --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_heading_artifact_analyzer.svg @@ -0,0 +1,256 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_heading_artifact_analyzer.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_heading_artifact_analyzer.svg.96dpi.png new file mode 100644 index 0000000000..1276f0938f Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_heading_artifact_analyzer.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_heading_captains_thoughts.svg b/Resources/Textures/Interface/Paper/paper_heading_captains_thoughts.svg new file mode 100644 index 0000000000..e52ca49143 --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_heading_captains_thoughts.svg @@ -0,0 +1,187 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_heading_captains_thoughts.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_heading_captains_thoughts.svg.96dpi.png new file mode 100644 index 0000000000..1ce991aaad Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_heading_captains_thoughts.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_heading_cargo_invoice.svg b/Resources/Textures/Interface/Paper/paper_heading_cargo_invoice.svg new file mode 100644 index 0000000000..726b4dbd73 --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_heading_cargo_invoice.svg @@ -0,0 +1,233 @@ + +image/svg+xml diff --git a/Resources/Textures/Interface/Paper/paper_heading_cargo_invoice.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_heading_cargo_invoice.svg.96dpi.png new file mode 100644 index 0000000000..52e36314b1 Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_heading_cargo_invoice.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_heading_virus.svg b/Resources/Textures/Interface/Paper/paper_heading_virus.svg new file mode 100644 index 0000000000..9895cc446f --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_heading_virus.svg @@ -0,0 +1,177 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_heading_virus.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_heading_virus.svg.96dpi.png new file mode 100644 index 0000000000..24a6bc6388 Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_heading_virus.svg.96dpi.png differ diff --git a/Resources/Textures/Objects/Misc/bureaucracy.rsi/meta.json b/Resources/Textures/Objects/Misc/bureaucracy.rsi/meta.json index c028ff6968..ed8e17060a 100644 --- a/Resources/Textures/Objects/Misc/bureaucracy.rsi/meta.json +++ b/Resources/Textures/Objects/Misc/bureaucracy.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. stamp-syndicate is a modified version of stamp-warden. paper_stamp-syndicate by Veritius.", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. stamp-syndicate is a modified version of stamp-warden. paper_stamp-syndicate by Veritius. paper_receipt by eoineoineoin", "size": { "x": 32, "y": 32 @@ -201,6 +201,18 @@ { "name": "paper_words-blood" }, + { + "name": "paper_receipt" + }, + { + "name": "paper_receipt_words" + }, + { + "name": "paper_dotmatrix" + }, + { + "name": "paper_dotmatrix_words" + }, { "name": "pen" }, @@ -209,8 +221,8 @@ }, { "name": "pen_hop" - }, - { + }, + { "name": "overpriced_pen" }, { diff --git a/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_dotmatrix.png b/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_dotmatrix.png new file mode 100644 index 0000000000..fe7637a7b8 Binary files /dev/null and b/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_dotmatrix.png differ diff --git a/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_dotmatrix_words.png b/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_dotmatrix_words.png new file mode 100644 index 0000000000..d3956b4a4f Binary files /dev/null and b/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_dotmatrix_words.png differ diff --git a/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_receipt.png b/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_receipt.png new file mode 100644 index 0000000000..70c7237ffc Binary files /dev/null and b/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_receipt.png differ diff --git a/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_receipt_words.png b/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_receipt_words.png new file mode 100644 index 0000000000..78d138a870 Binary files /dev/null and b/Resources/Textures/Objects/Misc/bureaucracy.rsi/paper_receipt_words.png differ