From 600621e7ee76ebaf240533f0f7b6f600766f6fda Mon Sep 17 00:00:00 2001 From: Phill101 <28949487+Phill101@users.noreply.github.com> Date: Sat, 5 Aug 2023 13:37:08 +0500 Subject: [PATCH] Crew manifest as a PDA cartridge program (#18498) Co-authored-by: Phill101 --- .../Cartridges/CrewManifestUi.cs | 29 ++++ .../Cartridges/CrewManifestUiFragment.xaml | 20 +++ .../Cartridges/CrewManifestUiFragment.xaml.cs | 34 ++++ .../CrewManifest/CrewManifestUi.xaml | 5 +- .../CrewManifest/CrewManifestUi.xaml.cs | 149 +----------------- .../CrewManifest/UI/CrewManifestListing.cs | 74 +++++++++ .../CrewManifest/UI/CrewManifestSection.cs | 74 +++++++++ Content.Client/PDA/PdaBoundUserInterface.cs | 13 -- Content.Client/PDA/PdaMenu.xaml | 5 - .../CartridgeLoader/CartridgeLoaderSystem.cs | 41 ++++- .../CrewManifestCartridgeComponent.cs | 6 + .../Cartridges/CrewManifestCartridgeSystem.cs | 95 +++++++++++ .../Cartridges/CrewManifestUiState.cs | 17 ++ .../en-US/cartridge-loader/cartridges.ftl | 3 + .../Entities/Objects/Devices/cartridges.yml | 27 ++-- .../Entities/Objects/Devices/pda.yml | 1 + .../Misc/program_icons.rsi/crew_manifest.png | Bin 0 -> 7183 bytes .../Misc/program_icons.rsi/meta.json | 5 +- 18 files changed, 418 insertions(+), 180 deletions(-) create mode 100644 Content.Client/CartridgeLoader/Cartridges/CrewManifestUi.cs create mode 100644 Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml create mode 100644 Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs create mode 100644 Content.Client/CrewManifest/UI/CrewManifestListing.cs create mode 100644 Content.Client/CrewManifest/UI/CrewManifestSection.cs create mode 100644 Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeComponent.cs create mode 100644 Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs create mode 100644 Content.Shared/CartridgeLoader/Cartridges/CrewManifestUiState.cs create mode 100644 Resources/Textures/Interface/Misc/program_icons.rsi/crew_manifest.png diff --git a/Content.Client/CartridgeLoader/Cartridges/CrewManifestUi.cs b/Content.Client/CartridgeLoader/Cartridges/CrewManifestUi.cs new file mode 100644 index 0000000000..c00e78c8d6 --- /dev/null +++ b/Content.Client/CartridgeLoader/Cartridges/CrewManifestUi.cs @@ -0,0 +1,29 @@ +using Content.Client.UserInterface.Fragments; +using Content.Shared.CartridgeLoader.Cartridges; +using Robust.Client.GameObjects; +using Robust.Client.UserInterface; + +namespace Content.Client.CartridgeLoader.Cartridges; + +public sealed class CrewManifestUi : UIFragment +{ + private CrewManifestUiFragment? _fragment; + + public override Control GetUIFragmentRoot() + { + return _fragment!; + } + + public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner) + { + _fragment = new CrewManifestUiFragment(); + } + + public override void UpdateState(BoundUserInterfaceState state) + { + if (state is not CrewManifestUiState crewManifestState) + return; + + _fragment?.UpdateState(crewManifestState.StationName, crewManifestState.Entries); + } +} diff --git a/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml b/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml new file mode 100644 index 0000000000..14b06c90ad --- /dev/null +++ b/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs b/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs new file mode 100644 index 0000000000..273707cb6e --- /dev/null +++ b/Content.Client/CartridgeLoader/Cartridges/CrewManifestUiFragment.xaml.cs @@ -0,0 +1,34 @@ +using Content.Shared.CrewManifest; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.CartridgeLoader.Cartridges; + +[GenerateTypedNameReferences] +public sealed partial class CrewManifestUiFragment : BoxContainer +{ + public CrewManifestUiFragment() + { + RobustXamlLoader.Load(this); + + StationName.AddStyleClass("LabelBig"); + Orientation = LayoutOrientation.Vertical; + HorizontalExpand = true; + VerticalExpand = true; + } + + public void UpdateState(string stationName, CrewManifestEntries? entries) + { + CrewManifestListing.DisposeAllChildren(); + CrewManifestListing.RemoveAllChildren(); + + StationNameContainer.Visible = entries != null; + StationName.Text = stationName; + + if (entries == null) + return; + + CrewManifestListing.AddCrewManifestEntries(entries); + } +} diff --git a/Content.Client/CrewManifest/CrewManifestUi.xaml b/Content.Client/CrewManifest/CrewManifestUi.xaml index b025e79d97..db3a7ac390 100644 --- a/Content.Client/CrewManifest/CrewManifestUi.xaml +++ b/Content.Client/CrewManifest/CrewManifestUi.xaml @@ -1,5 +1,6 @@ @@ -11,9 +12,9 @@ - + + diff --git a/Content.Client/CrewManifest/CrewManifestUi.xaml.cs b/Content.Client/CrewManifest/CrewManifestUi.xaml.cs index 5cfead0d8b..4183c90814 100644 --- a/Content.Client/CrewManifest/CrewManifestUi.xaml.cs +++ b/Content.Client/CrewManifest/CrewManifestUi.xaml.cs @@ -1,37 +1,16 @@ -using System.Linq; -using System.Numerics; -using Content.Shared.CCVar; using Content.Shared.CrewManifest; -using Content.Shared.Roles; -using Content.Shared.StatusIcon; using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; -using Robust.Client.ResourceManagement; -using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; -using Robust.Client.Utility; -using Robust.Shared.Configuration; -using Robust.Shared.Prototypes; -using Robust.Shared.Utility; namespace Content.Client.CrewManifest; [GenerateTypedNameReferences] public sealed partial class CrewManifestUi : DefaultWindow { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IResourceCache _resourceCache = default!; - [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; - [Dependency] private readonly IConfigurationManager _configManager = default!; - - private readonly CrewManifestSystem _crewManifestSystem; - public CrewManifestUi() { RobustXamlLoader.Load(this); - IoCManager.InjectDependencies(this); - _crewManifestSystem = _entitySystemManager.GetEntitySystem(); StationName.AddStyleClass("LabelBig"); } @@ -44,131 +23,9 @@ public sealed partial class CrewManifestUi : DefaultWindow StationNameContainer.Visible = entries != null; StationName.Text = name; - if (entries == null) return; + if (entries == null) + return; - var entryList = SortEntries(entries); - - foreach (var item in entryList) - { - CrewManifestListing.AddChild(new CrewManifestSection(item.section, item.entries, _resourceCache, _crewManifestSystem)); - } - } - - private List<(string section, List entries)> SortEntries(CrewManifestEntries entries) - { - var entryDict = new Dictionary>(); - - foreach (var entry in entries.Entries) - { - foreach (var department in _prototypeManager.EnumeratePrototypes()) - { - // this is a little expensive, and could be better - if (department.Roles.Contains(entry.JobPrototype)) - { - entryDict.GetOrNew(department.ID).Add(entry); - } - } - } - - var entryList = new List<(string section, List entries)>(); - - foreach (var (section, listing) in entryDict) - { - entryList.Add((section, listing)); - } - - var sortOrder = _configManager.GetCVar(CCVars.CrewManifestOrdering).Split(",").ToList(); - - entryList.Sort((a, b) => - { - var ai = sortOrder.IndexOf(a.section); - var bi = sortOrder.IndexOf(b.section); - - // this is up here so -1 == -1 occurs first - if (ai == bi) - return 0; - - if (ai == -1) - return -1; - - if (bi == -1) - return 1; - - return ai.CompareTo(bi); - }); - - return entryList; - } - - private sealed class CrewManifestSection : BoxContainer - { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IEntitySystemManager _entitySystem = default!; - private readonly SpriteSystem _spriteSystem = default!; - - public CrewManifestSection(string sectionTitle, List entries, IResourceCache cache, CrewManifestSystem crewManifestSystem) - { - IoCManager.InjectDependencies(this); - _spriteSystem = _entitySystem.GetEntitySystem(); - - Orientation = LayoutOrientation.Vertical; - HorizontalExpand = true; - - if (Loc.TryGetString($"department-{sectionTitle}", out var localizedDepart)) - sectionTitle = localizedDepart; - - AddChild(new Label() - { - StyleClasses = { "LabelBig" }, - Text = Loc.GetString(sectionTitle) - }); - - var gridContainer = new GridContainer() - { - HorizontalExpand = true, - Columns = 2 - }; - - AddChild(gridContainer); - - foreach (var entry in entries) - { - var name = new RichTextLabel() - { - HorizontalExpand = true, - }; - name.SetMessage(entry.Name); - - var titleContainer = new BoxContainer() - { - Orientation = LayoutOrientation.Horizontal, - HorizontalExpand = true - }; - - var title = new RichTextLabel(); - title.SetMessage(entry.JobTitle); - - - if (_prototypeManager.TryIndex(entry.JobIcon, out var jobIcon)) - { - var icon = new TextureRect() - { - TextureScale = new Vector2(2, 2), - Stretch = TextureRect.StretchMode.KeepCentered, - Texture = _spriteSystem.Frame0(jobIcon.Icon), - }; - - titleContainer.AddChild(icon); - titleContainer.AddChild(title); - } - else - { - titleContainer.AddChild(title); - } - - gridContainer.AddChild(name); - gridContainer.AddChild(titleContainer); - } - } + CrewManifestListing.AddCrewManifestEntries(entries); } } diff --git a/Content.Client/CrewManifest/UI/CrewManifestListing.cs b/Content.Client/CrewManifest/UI/CrewManifestListing.cs new file mode 100644 index 0000000000..614add536e --- /dev/null +++ b/Content.Client/CrewManifest/UI/CrewManifestListing.cs @@ -0,0 +1,74 @@ +using Content.Shared.CCVar; +using Content.Shared.CrewManifest; +using Content.Shared.Roles; +using Robust.Client.GameObjects; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Configuration; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; +using System.Linq; + +namespace Content.Client.CrewManifest.UI; + +public sealed class CrewManifestListing : BoxContainer +{ + [Dependency] private readonly IConfigurationManager _configManager = default!; + [Dependency] private readonly IEntitySystemManager _entitySystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + private readonly SpriteSystem _spriteSystem; + + public CrewManifestListing() + { + IoCManager.InjectDependencies(this); + _spriteSystem = _entitySystem.GetEntitySystem(); + } + + public void AddCrewManifestEntries(CrewManifestEntries entries) + { + var entryDict = new Dictionary>(); + + foreach (var entry in entries.Entries) + { + foreach (var department in _prototypeManager.EnumeratePrototypes()) + { + // this is a little expensive, and could be better + if (department.Roles.Contains(entry.JobPrototype)) + { + entryDict.GetOrNew(department.ID).Add(entry); + } + } + } + + var entryList = new List<(string section, List entries)>(); + + foreach (var (section, listing) in entryDict) + { + entryList.Add((section, listing)); + } + + var sortOrder = _configManager.GetCVar(CCVars.CrewManifestOrdering).Split(",").ToList(); + + entryList.Sort((a, b) => + { + var ai = sortOrder.IndexOf(a.section); + var bi = sortOrder.IndexOf(b.section); + + // this is up here so -1 == -1 occurs first + if (ai == bi) + return 0; + + if (ai == -1) + return -1; + + if (bi == -1) + return 1; + + return ai.CompareTo(bi); + }); + + foreach (var item in entryList) + { + AddChild(new CrewManifestSection(_prototypeManager, _spriteSystem, item.section, item.entries)); + } + } +} diff --git a/Content.Client/CrewManifest/UI/CrewManifestSection.cs b/Content.Client/CrewManifest/UI/CrewManifestSection.cs new file mode 100644 index 0000000000..9b51b5d424 --- /dev/null +++ b/Content.Client/CrewManifest/UI/CrewManifestSection.cs @@ -0,0 +1,74 @@ +using Content.Shared.CrewManifest; +using Content.Shared.StatusIcon; +using Robust.Client.GameObjects; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Prototypes; +using System.Numerics; + +namespace Content.Client.CrewManifest.UI; + +public sealed class CrewManifestSection : BoxContainer +{ + public CrewManifestSection(IPrototypeManager prototypeManager, SpriteSystem spriteSystem, string sectionTitle, + List entries) + { + Orientation = LayoutOrientation.Vertical; + HorizontalExpand = true; + + if (Loc.TryGetString($"department-{sectionTitle}", out var localizedDepart)) + sectionTitle = localizedDepart; + + AddChild(new Label() + { + StyleClasses = { "LabelBig" }, + Text = Loc.GetString(sectionTitle) + }); + + var gridContainer = new GridContainer() + { + HorizontalExpand = true, + Columns = 2 + }; + + AddChild(gridContainer); + + foreach (var entry in entries) + { + var name = new RichTextLabel() + { + HorizontalExpand = true, + }; + name.SetMessage(entry.Name); + + var titleContainer = new BoxContainer() + { + Orientation = LayoutOrientation.Horizontal, + HorizontalExpand = true + }; + + var title = new RichTextLabel(); + title.SetMessage(entry.JobTitle); + + + if (prototypeManager.TryIndex(entry.JobIcon, out var jobIcon)) + { + var icon = new TextureRect() + { + TextureScale = new Vector2(2, 2), + Stretch = TextureRect.StretchMode.KeepCentered, + Texture = spriteSystem.Frame0(jobIcon.Icon), + }; + + titleContainer.AddChild(icon); + titleContainer.AddChild(title); + } + else + { + titleContainer.AddChild(title); + } + + gridContainer.AddChild(name); + gridContainer.AddChild(titleContainer); + } + } +} diff --git a/Content.Client/PDA/PdaBoundUserInterface.cs b/Content.Client/PDA/PdaBoundUserInterface.cs index bd23d5ea55..059d3c0aef 100644 --- a/Content.Client/PDA/PdaBoundUserInterface.cs +++ b/Content.Client/PDA/PdaBoundUserInterface.cs @@ -1,8 +1,6 @@ using Content.Client.CartridgeLoader; using Content.Shared.CartridgeLoader; -using Content.Shared.CCVar; using Content.Shared.Containers.ItemSlots; -using Content.Shared.CrewManifest; using Content.Shared.PDA; using JetBrains.Annotations; using Robust.Client.UserInterface; @@ -13,8 +11,6 @@ namespace Content.Client.PDA [UsedImplicitly] public sealed class PdaBoundUserInterface : CartridgeLoaderBoundUserInterface { - [Dependency] private readonly IConfigurationManager _configManager = default!; - [ViewVariables] private PdaMenu? _menu; @@ -34,15 +30,6 @@ namespace Content.Client.PDA SendMessage(new PdaToggleFlashlightMessage()); }; - if (_configManager.GetCVar(CCVars.CrewManifestUnsecure)) - { - _menu.CrewManifestButton.Visible = true; - _menu.CrewManifestButton.OnPressed += _ => - { - SendMessage(new CrewManifestOpenUiMessage()); - }; - } - _menu.EjectIdButton.OnPressed += _ => { SendMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaIdSlotId)); diff --git a/Content.Client/PDA/PdaMenu.xaml b/Content.Client/PDA/PdaMenu.xaml index dbdcb4d90b..9f9dad6640 100644 --- a/Content.Client/PDA/PdaMenu.xaml +++ b/Content.Client/PDA/PdaMenu.xaml @@ -60,11 +60,6 @@ Access="Public" Text="{Loc 'comp-pda-ui-ringtone-button'}" Description="{Loc 'comp-pda-ui-ringtone-button-description'}"/> - (); //Don't count a cartridge that has already been installed as available to avoid confusion - if (loader.CartridgeSlot.HasItem && IsInstalled(Prototype(loader.CartridgeSlot.Item!.Value)?.ID, loader)) + if (loader.CartridgeSlot.HasItem && TryFindInstalled(Prototype(loader.CartridgeSlot.Item!.Value)?.ID, loader, out _)) return loader.InstalledPrograms; var available = new List(); @@ -127,7 +128,13 @@ public sealed class CartridgeLoaderSystem : SharedCartridgeLoaderSystem return false; //Prevent installing cartridges that have already been installed - if (IsInstalled(prototype, loader)) + if (TryFindInstalled(prototype, loader, out _)) + return false; + + var ev = new ProgramInstallationAttempt(loaderUid, prototype); + RaiseLocalEvent(ref ev); + + if (ev.Cancelled) return false; var installedProgram = Spawn(prototype, new EntityCoordinates(loaderUid, 0, 0)); @@ -141,6 +148,22 @@ public sealed class CartridgeLoaderSystem : SharedCartridgeLoaderSystem return true; } + /// + /// Uninstalls a program using its prototype + /// + /// The cartridge loader uid + /// The prototype name of the program to be uninstalled + /// The cartridge loader component + /// Whether uninstalling the program was successful + public bool UninstallProgram(EntityUid loaderUid, string prototype, CartridgeLoaderComponent? loader = default!) + { + if (!Resolve(loaderUid, ref loader)) + return false; + + return TryFindInstalled(prototype, loader, out var programUid) && + UninstallProgram(loaderUid, programUid.Value, loader); + } + /// /// Uninstalls a program using its uid /// @@ -345,16 +368,20 @@ public sealed class CartridgeLoaderSystem : SharedCartridgeLoaderSystem } /// - /// Checks if a program is already installed by searching for its prototype name in the list of installed programs + /// Searches for a program by its prototype name in the list of installed programs /// - private bool IsInstalled(string? prototype, CartridgeLoaderComponent loader) + private bool TryFindInstalled(string? prototype, CartridgeLoaderComponent loader, [NotNullWhen(true)] out EntityUid? programUid) { foreach (var program in loader.InstalledPrograms) { if (Prototype(program)?.ID == prototype) + { + programUid = program; return true; + } } + programUid = default; return false; } @@ -414,3 +441,9 @@ public sealed class CartridgeAfterInteractEvent : EntityEventArgs InteractEvent = interactEvent; } } + +/// +/// Raised on an attempt of program installation. +/// +[ByRefEvent] +public record struct ProgramInstallationAttempt(EntityUid LoaderUid, string Prototype, bool Cancelled = false); diff --git a/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeComponent.cs b/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeComponent.cs new file mode 100644 index 0000000000..344f8674e3 --- /dev/null +++ b/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.CartridgeLoader.Cartridges; + +[RegisterComponent] +public sealed class CrewManifestCartridgeComponent : Component +{ +} diff --git a/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs b/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs new file mode 100644 index 0000000000..74757c1f79 --- /dev/null +++ b/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs @@ -0,0 +1,95 @@ +using Content.Server.CrewManifest; +using Content.Server.Station.Systems; +using Content.Shared.CartridgeLoader; +using Content.Shared.CartridgeLoader.Cartridges; +using Content.Shared.CCVar; +using Robust.Shared.Configuration; + +namespace Content.Server.CartridgeLoader.Cartridges; + +public sealed class CrewManifestCartridgeSystem : EntitySystem +{ + [Dependency] private readonly CartridgeLoaderSystem _cartridgeLoader = default!; + [Dependency] private readonly IConfigurationManager _configManager = default!; + [Dependency] private readonly CrewManifestSystem _crewManifest = default!; + [Dependency] private readonly StationSystem _stationSystem = default!; + + private const string CartridgePrototypeName = "CrewManifestCartridge"; + + /// + /// Flag that shows that if crew manifest is allowed to be viewed from 'unsecure' entities, + /// which is the keys for the cartridge. + /// + private bool _unsecureViewersAllowed = true; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnUiMessage); + SubscribeLocalEvent(OnUiReady); + SubscribeLocalEvent(OnInstallationAttempt); + _configManager.OnValueChanged(CCVars.CrewManifestUnsecure, OnCrewManifestUnsecureChanged, true); + } + + /// + /// The ui messages received here get wrapped by a CartridgeMessageEvent and are relayed from the + /// + /// + /// The cartridge specific ui message event needs to inherit from the CartridgeMessageEvent + /// + private void OnUiMessage(EntityUid uid, CrewManifestCartridgeComponent component, CartridgeMessageEvent args) + { + UpdateUiState(uid, args.LoaderUid, component); + } + + /// + /// This gets called when the ui fragment needs to be updated for the first time after activating + /// + private void OnUiReady(EntityUid uid, CrewManifestCartridgeComponent component, CartridgeUiReadyEvent args) + { + UpdateUiState(uid, args.Loader, component); + } + + private void UpdateUiState(EntityUid uid, EntityUid loaderUid, CrewManifestCartridgeComponent? component) + { + if (!Resolve(uid, ref component)) + return; + + var owningStation = _stationSystem.GetOwningStation(uid); + + if (owningStation is null) + return; + + var (stationName, entries) = _crewManifest.GetCrewManifest(owningStation.Value); + + var state = new CrewManifestUiState(stationName, entries); + _cartridgeLoader.UpdateCartridgeUiState(loaderUid, state); + } + + private void OnInstallationAttempt(ref ProgramInstallationAttempt args) + { + if (args.Prototype == CartridgePrototypeName && !_unsecureViewersAllowed) + args.Cancelled = true; + } + + private void OnCrewManifestUnsecureChanged(bool unsecureViewersAllowed) + { + _unsecureViewersAllowed = unsecureViewersAllowed; + + var allCartridgeLoaders = AllEntityQuery(); + + while (allCartridgeLoaders.MoveNext(out EntityUid loaderUid, out CartridgeLoaderComponent? comp)) + { + if (_unsecureViewersAllowed) + _cartridgeLoader?.InstallProgram(loaderUid, CartridgePrototypeName, false, comp); + else + _cartridgeLoader?.UninstallProgram(loaderUid, CartridgePrototypeName, comp); + } + } + + public override void Shutdown() + { + base.Shutdown(); + _configManager.UnsubValueChanged(CCVars.CrewManifestUnsecure, OnCrewManifestUnsecureChanged); + } +} diff --git a/Content.Shared/CartridgeLoader/Cartridges/CrewManifestUiState.cs b/Content.Shared/CartridgeLoader/Cartridges/CrewManifestUiState.cs new file mode 100644 index 0000000000..9eaca5a2d3 --- /dev/null +++ b/Content.Shared/CartridgeLoader/Cartridges/CrewManifestUiState.cs @@ -0,0 +1,17 @@ +using Content.Shared.CrewManifest; +using Robust.Shared.Serialization; + +namespace Content.Shared.CartridgeLoader.Cartridges; + +[Serializable, NetSerializable] +public sealed class CrewManifestUiState : BoundUserInterfaceState +{ + public string StationName; + public CrewManifestEntries? Entries; + + public CrewManifestUiState(string stationName, CrewManifestEntries? entries) + { + StationName = stationName; + Entries = entries; + } +} diff --git a/Resources/Locale/en-US/cartridge-loader/cartridges.ftl b/Resources/Locale/en-US/cartridge-loader/cartridges.ftl index 3be0fdcce0..f324da7be6 100644 --- a/Resources/Locale/en-US/cartridge-loader/cartridges.ftl +++ b/Resources/Locale/en-US/cartridge-loader/cartridges.ftl @@ -2,6 +2,9 @@ notekeeper-program-name = Notekeeper news-read-program-name = Station news +crew-manifest-program-name = Crew manifest +crew-manifest-cartridge-loading = Loading ... + net-probe-program-name = NetProbe net-probe-scan = Scanned {$device}! net-probe-label-name = Name diff --git a/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml b/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml index 78092d414e..739e0f394f 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml @@ -7,9 +7,6 @@ - type: Sprite sprite: Objects/Devices/cartridge.rsi state: cart-y - - type: Icon - sprite: Objects/Devices/cartridge.rsi - state: cart-y - type: UIFragment ui: !type:NotekeeperUi - type: Cartridge @@ -28,9 +25,6 @@ - type: Sprite sprite: Objects/Devices/cartridge.rsi state: cart-y - - type: Icon - sprite: Objects/Devices/cartridge.rsi - state: cart-y - type: UIFragment ui: !type:NewsReadUi - type: Cartridge @@ -40,6 +34,24 @@ state: news_read - type: NewsReadCartridge +- type: entity + parent: BaseItem + id: CrewManifestCartridge + name: Crew Manifest Cartridge + description: A program for listing your fellow crewmembers + components: + - type: Sprite + sprite: Objects/Devices/cartridge.rsi + state: cart-y + - type: UIFragment + ui: !type:CrewManifestUi + - type: Cartridge + programName: crew-manifest-program-name + icon: + sprite: Interface/Misc/program_icons.rsi + state: crew_manifest + - type: CrewManifestCartridge + - type: entity parent: BaseItem id: NetProbeCartridge @@ -49,9 +61,6 @@ - type: Sprite sprite: Objects/Devices/cartridge.rsi state: cart-y - - type: Icon - sprite: Objects/Devices/cartridge.rsi - state: cart-y - type: UIFragment ui: !type:NetProbeUi - type: Cartridge diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 161a68124c..53bf43f742 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -72,6 +72,7 @@ - type: CartridgeLoader uiKey: enum.PdaUiKey.Key preinstalled: + - CrewManifestCartridge - NotekeeperCartridge - NewsReadCartridge cartridgeSlot: diff --git a/Resources/Textures/Interface/Misc/program_icons.rsi/crew_manifest.png b/Resources/Textures/Interface/Misc/program_icons.rsi/crew_manifest.png new file mode 100644 index 0000000000000000000000000000000000000000..1896ffcdfa305c974b4434d9203eef463c93cf0f GIT binary patch literal 7183 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 IdNB5X0J-V9-v9sr literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Misc/program_icons.rsi/meta.json b/Resources/Textures/Interface/Misc/program_icons.rsi/meta.json index 5ed7f67860..4c2a45ad76 100644 --- a/Resources/Textures/Interface/Misc/program_icons.rsi/meta.json +++ b/Resources/Textures/Interface/Misc/program_icons.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made with love, by Misha_Unity", + "copyright": "news_read by Misha_Unity, crew_manifest by Phill101", "size": { "x": 32, "y": 32 @@ -9,6 +9,9 @@ "states": [ { "name": "news_read" + }, + { + "name": "crew_manifest" } ] }