diff --git a/Content.Client/MassMedia/Ui/NewsReadMenu.xaml.cs b/Content.Client/MassMedia/Ui/NewsReadMenu.xaml.cs index f92a21794b..1b02df34b3 100644 --- a/Content.Client/MassMedia/Ui/NewsReadMenu.xaml.cs +++ b/Content.Client/MassMedia/Ui/NewsReadMenu.xaml.cs @@ -1,18 +1,14 @@ using Content.Client.Message; +using Content.Shared.MassMedia.Systems; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Prototypes; -using Content.Shared.MassMedia.Systems; namespace Content.Client.MassMedia.Ui; [GenerateTypedNameReferences] public sealed partial class NewsReadMenu : DefaultWindow { - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - public event Action? NextButtonPressed; public event Action? PastButtonPressed; @@ -34,13 +30,13 @@ public sealed partial class NewsReadMenu : DefaultWindow PageText.Visible = true; ShareTime.Visible = true; - PageName.Text = article.Name; + PageName.Text = $"{article.Name} by {article.Author ?? Loc.GetString("news-read-ui-no-author")}"; PageText.SetMarkup(article.Content); PageNum.Text = $"{targetNum}/{totalNum}"; string shareTime = article.ShareTime.ToString("hh\\:mm\\:ss"); - ShareTime.SetMarkup(Loc.GetString("news-read-ui-time-prefix-text") + " " + shareTime); + ShareTime.SetMarkup($"{Loc.GetString("news-read-ui-time-prefix-text")} {shareTime}"); } public void UpdateEmptyUI() diff --git a/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs b/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs index 2e130a428a..9b228efddd 100644 --- a/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs +++ b/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs @@ -34,7 +34,7 @@ namespace Content.Client.MassMedia.Ui _menu.OnClose += Close; _menu.ShareButtonPressed += OnShareButtonPressed; - _menu.DeleteButtonPressed += num => OnDeleteButtonPressed(num); + _menu.DeleteButtonPressed += OnDeleteButtonPressed; _gameTicker = _entitySystem.GetEntitySystem(); diff --git a/Content.Client/MassMedia/Ui/NewsWriteMenu.xaml b/Content.Client/MassMedia/Ui/NewsWriteMenu.xaml index 0b03078812..08d113f8a9 100644 --- a/Content.Client/MassMedia/Ui/NewsWriteMenu.xaml +++ b/Content.Client/MassMedia/Ui/NewsWriteMenu.xaml @@ -28,14 +28,10 @@ Name="ArticleCardsContainer" Orientation="Vertical" VerticalExpand="True"> - - - - Articles = new List(); + private readonly List _articles = new List(); public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnWriteUiMessage); - SubscribeLocalEvent(OnWriteUiMessage); - SubscribeLocalEvent(OnWriteUiMessage); - SubscribeLocalEvent(OnReadUiMessage); - SubscribeLocalEvent(OnReadUiMessage); + SubscribeLocalEvent(OnDeleteUiMessage); + SubscribeLocalEvent(OnRequestUiMessage); + SubscribeLocalEvent(OnNextArticleUiMessage); + SubscribeLocalEvent(OnPrevArticleUiMessage); SubscribeLocalEvent(OnReadUiMessage); SubscribeLocalEvent(OnRoundRestart); @@ -31,7 +36,7 @@ public sealed class NewsSystem : EntitySystem private void OnRoundRestart(RoundRestartCleanupEvent ev) { - if (Articles != null) Articles.Clear(); + _articles?.Clear(); } public void ToggleUi(EntityUid user, EntityUid deviceEnt, NewsReadComponent? component) @@ -56,12 +61,12 @@ public sealed class NewsSystem : EntitySystem _ui.TryToggleUi(deviceEnt, NewsWriteUiKey.Key, actor.PlayerSession); } - public void UpdateWriteUi(EntityUid uid, NewsWriteComponent component) + public void UpdateWriteUi(EntityUid uid) { if (!_ui.TryGetUi(uid, NewsWriteUiKey.Key, out _)) return; - var state = new NewsWriteBoundUserInterfaceState(Articles.ToArray()); + var state = new NewsWriteBoundUserInterfaceState(_articles.ToArray()); _ui.TrySetUiState(uid, NewsWriteUiKey.Key, state); } @@ -70,49 +75,103 @@ public sealed class NewsSystem : EntitySystem if (!_ui.TryGetUi(uid, NewsReadUiKey.Key, out _)) return; - if (component.ArticleNum < 0) NewsReadLeafArticle(component, -1); + if (component.ArticleNum < 0) + { + NewsReadPreviousArticle(component); + } - if (Articles.Any()) - _ui.TrySetUiState(uid, NewsReadUiKey.Key, new NewsReadBoundUserInterfaceState(Articles[component.ArticleNum], component.ArticleNum + 1, Articles.Count)); + if (_articles.Any()) + { + if (component.ArticleNum >= _articles.Count) + { + component.ArticleNum = _articles.Count - 1; + } + + _ui.TrySetUiState(uid, NewsReadUiKey.Key, new NewsReadBoundUserInterfaceState(_articles[component.ArticleNum], component.ArticleNum + 1, _articles.Count)); + } else + { _ui.TrySetUiState(uid, NewsReadUiKey.Key, new NewsReadEmptyBoundUserInterfaceState()); + } } public void OnWriteUiMessage(EntityUid uid, NewsWriteComponent component, NewsWriteShareMessage msg) { - Articles.Add(msg.Article); + var article = msg.Article; + + var author = msg.Session.AttachedEntity; + if (author.HasValue + && _accessReader.FindAccessItemsInventory(author.Value, out var items) + && _accessReader.FindStationRecordKeys(author.Value, out var stationRecordKeys, items)) + { + article.AuthorStationRecordKeyIds = stationRecordKeys; + + foreach (var item in items) + { + // ID Card + if (TryComp(item, out IdCardComponent? id)) + { + article.Author = id.FullName; + break; + } + // PDA + else if (TryComp(item, out PdaComponent? pda) + && pda.ContainedId != null + && TryComp(pda.ContainedId, out id)) + { + article.Author = id.FullName; + break; + } + } + } + + _articles.Add(article); UpdateReadDevices(); UpdateWriteDevices(); TryNotify(); } - public void OnWriteUiMessage(EntityUid uid, NewsWriteComponent component, NewsWriteDeleteMessage msg) + public void OnDeleteUiMessage(EntityUid uid, NewsWriteComponent component, NewsWriteDeleteMessage msg) { - if (msg.ArticleNum < Articles.Count) + if (msg.ArticleNum > _articles.Count) + return; + + var articleToDelete = _articles[msg.ArticleNum]; + if (articleToDelete.AuthorStationRecordKeyIds == null || !articleToDelete.AuthorStationRecordKeyIds.Any()) { - Articles.RemoveAt(msg.ArticleNum); + _articles.RemoveAt(msg.ArticleNum); + } + else + { + var author = msg.Session.AttachedEntity; + if (author.HasValue + && _accessReader.FindStationRecordKeys(author.Value, out var recordKeys) + && recordKeys.Intersect(articleToDelete.AuthorStationRecordKeyIds).Any()) + { + _articles.RemoveAt(msg.ArticleNum); + } } UpdateReadDevices(); UpdateWriteDevices(); } - public void OnWriteUiMessage(EntityUid uid, NewsWriteComponent component, NewsWriteArticlesRequestMessage msg) + public void OnRequestUiMessage(EntityUid uid, NewsWriteComponent component, NewsWriteArticlesRequestMessage msg) { - UpdateWriteUi(uid, component); + UpdateWriteUi(uid); } - public void OnReadUiMessage(EntityUid uid, NewsReadComponent component, NewsReadNextMessage msg) + public void OnNextArticleUiMessage(EntityUid uid, NewsReadComponent component, NewsReadNextMessage msg) { - NewsReadLeafArticle(component, 1); + NewsReadNextArticle(component); UpdateReadUi(uid, component); } - public void OnReadUiMessage(EntityUid uid, NewsReadComponent component, NewsReadPrevMessage msg) + public void OnPrevArticleUiMessage(EntityUid uid, NewsReadComponent component, NewsReadPrevMessage msg) { - NewsReadLeafArticle(component, -1); + NewsReadPreviousArticle(component); UpdateReadUi(uid, component); } @@ -122,21 +181,33 @@ public sealed class NewsSystem : EntitySystem UpdateReadUi(uid, component); } - private void NewsReadLeafArticle(NewsReadComponent component, int leafDir) + private void NewsReadNextArticle(NewsReadComponent component) { - component.ArticleNum += leafDir; + if (!_articles.Any()) + { + return; + } - if (component.ArticleNum >= Articles.Count) component.ArticleNum = 0; - if (component.ArticleNum < 0) component.ArticleNum = Articles.Count - 1; + component.ArticleNum = (component.ArticleNum + 1) % _articles.Count; + } + + private void NewsReadPreviousArticle(NewsReadComponent component) + { + if (!_articles.Any()) + { + return; + } + + component.ArticleNum = (component.ArticleNum - 1 + _articles.Count) % _articles.Count; } private void TryNotify() { var query = EntityQueryEnumerator(); - while (query.MoveNext(out var comp, out var ringer)) + while (query.MoveNext(out var owner, out var _, out var ringer)) { - _ringer.RingerPlayRingtone(comp.Owner, ringer); + _ringer.RingerPlayRingtone(owner, ringer); } } @@ -144,9 +215,9 @@ public sealed class NewsSystem : EntitySystem { var query = EntityQueryEnumerator(); - while (query.MoveNext(out var comp)) + while (query.MoveNext(out var owner, out var comp)) { - UpdateReadUi(comp.Owner, comp); + UpdateReadUi(owner, comp); } } @@ -154,9 +225,9 @@ public sealed class NewsSystem : EntitySystem { var query = EntityQueryEnumerator(); - while (query.MoveNext(out var comp)) + while (query.MoveNext(out var owner, out var _)) { - UpdateWriteUi(comp.Owner, comp); + UpdateWriteUi(owner); } } } diff --git a/Content.Server/PDA/PdaSystem.cs b/Content.Server/PDA/PdaSystem.cs index e02e79ddd9..6168ad8d41 100644 --- a/Content.Server/PDA/PdaSystem.cs +++ b/Content.Server/PDA/PdaSystem.cs @@ -4,19 +4,19 @@ using Content.Server.DeviceNetwork.Components; using Content.Server.Instruments; using Content.Server.Light.EntitySystems; using Content.Server.Light.Events; +using Content.Server.MassMedia.Components; +using Content.Server.MassMedia.Systems; using Content.Server.Mind; using Content.Server.PDA.Ringer; using Content.Server.Station.Systems; using Content.Server.Store.Components; using Content.Server.Store.Systems; using Content.Shared.Access.Components; +using Content.Shared.Light.Component; using Content.Shared.PDA; using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Containers; -using Content.Shared.Light.Component; -using Content.Server.MassMedia.Components; -using Content.Server.MassMedia.Systems; namespace Content.Server.PDA { @@ -30,7 +30,6 @@ namespace Content.Server.PDA [Dependency] private readonly NewsSystem _news = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly UnpoweredFlashlightSystem _unpoweredFlashlight = default!; - [Dependency] private readonly MindSystem _mindSystem = default!; public override void Initialize() { diff --git a/Content.Server/PDA/Ringer/RingerComponent.cs b/Content.Server/PDA/Ringer/RingerComponent.cs index 2d70f78a5b..5ef26a8426 100644 --- a/Content.Server/PDA/Ringer/RingerComponent.cs +++ b/Content.Server/PDA/Ringer/RingerComponent.cs @@ -22,7 +22,7 @@ namespace Content.Server.PDA.Ringer /// [ViewVariables(VVAccess.ReadWrite)] [DataField("range")] - public float Range = 0.5f; + public float Range = 3f; [ViewVariables(VVAccess.ReadWrite)] [DataField("volume")] diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index f01ec061d7..b1789689e6 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -1,16 +1,16 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Content.Shared.Inventory; -using Content.Shared.Emag.Components; -using Content.Shared.Emag.Systems; -using Content.Shared.PDA; using Content.Shared.Access.Components; using Content.Shared.DeviceLinking.Events; -using Robust.Shared.Containers; -using Robust.Shared.Prototypes; +using Content.Shared.Emag.Components; +using Content.Shared.Emag.Systems; using Content.Shared.Hands.EntitySystems; +using Content.Shared.Inventory; +using Content.Shared.PDA; using Content.Shared.StationRecords; +using Robust.Shared.Containers; using Robust.Shared.GameStates; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Linq; namespace Content.Shared.Access.Systems; @@ -42,9 +42,9 @@ public sealed class AccessReaderSystem : EntitySystem if (args.Current is not AccessReaderComponentState state) return; component.Enabled = state.Enabled; - component.AccessKeys = new (state.AccessKeys); - component.AccessLists = new (state.AccessLists); - component.DenyTags = new (state.DenyTags); + component.AccessKeys = new(state.AccessKeys); + component.AccessLists = new(state.AccessLists); + component.DenyTags = new(state.DenyTags); } private void OnLinkAttempt(EntityUid uid, AccessReaderComponent component, LinkAttemptEvent args) @@ -67,24 +67,24 @@ public sealed class AccessReaderSystem : EntitySystem /// required entity. /// /// The entity to search for a container - private bool FindAccessReadersInContainer(EntityUid target, AccessReaderComponent accessReader, out List result) + private bool FindAccessReadersInContainer(EntityUid target, AccessReaderComponent accessReader, out List result) + { + result = new(); + if (accessReader.ContainerAccessProvider == null) + return false; + + if (!_containerSystem.TryGetContainer(target, accessReader.ContainerAccessProvider, out var container)) + return false; + + foreach (var entity in container.ContainedEntities) { - result = new(); - if (accessReader.ContainerAccessProvider == null) - return false; - - if (!_containerSystem.TryGetContainer(target, accessReader.ContainerAccessProvider, out var container)) - return false; - - foreach (var entity in container.ContainedEntities) - { - if (TryComp(entity, out var entityAccessReader)) - result.Add(entityAccessReader); - } - - return result.Any(); + if (TryComp(entity, out var entityAccessReader)) + result.Add(entityAccessReader); } + return result.Any(); + } + /// /// Searches the source for access tags /// then compares it with the all targets accesses to see if it is allowed. @@ -94,22 +94,22 @@ public sealed class AccessReaderSystem : EntitySystem /// Optional reader from the target entity public bool IsAllowed(EntityUid source, EntityUid target, AccessReaderComponent? reader = null) { - if (!Resolve(target, ref reader, false)) - return true; + if (!Resolve(target, ref reader, false)) + return true; - if (FindAccessReadersInContainer(target, reader, out var accessReaderList)) + if (FindAccessReadersInContainer(target, reader, out var accessReaderList)) + { + foreach (var access in accessReaderList) { - foreach (var access in accessReaderList) - { - if (IsAllowed(source, access)) - return true; - } - - return false; + if (IsAllowed(source, access)) + return true; } - return IsAllowed(source, reader); + return false; } + + return IsAllowed(source, reader); + } /// /// Searches the given entity for access tags /// then compares it with the readers access list to see if it is allowed. @@ -118,16 +118,17 @@ public sealed class AccessReaderSystem : EntitySystem /// A reader from a different entity public bool IsAllowed(EntityUid entity, AccessReaderComponent reader) { - var allEnts = FindPotentialAccessItems(entity); - // Access reader is totally disabled, so access is always allowed. if (!reader.Enabled) return true; + var allEnts = FindPotentialAccessItems(entity); + if (AreAccessTagsAllowed(FindAccessTags(entity, allEnts), reader)) return true; - if (AreStationRecordKeysAllowed(FindStationRecordKeys(entity, allEnts), reader)) + if (FindStationRecordKeys(entity, out var recordKeys, allEnts) + && AreStationRecordKeysAllowed(recordKeys, reader)) return true; return false; @@ -202,19 +203,19 @@ public sealed class AccessReaderSystem : EntitySystem /// /// The entity that is being searched. /// All of the items to search for access. If none are passed in, will be used. - public ICollection FindStationRecordKeys(EntityUid uid, HashSet? items = null) + public bool FindStationRecordKeys(EntityUid uid, out ICollection recordKeys, HashSet? items = null) { - HashSet keys = new(); + recordKeys = new HashSet(); items ??= FindPotentialAccessItems(uid); foreach (var ent in items) { if (FindStationRecordKeyItem(ent, out var key)) - keys.Add(key.Value); + recordKeys.Add(key.Value); } - return keys; + return recordKeys.Any(); } /// diff --git a/Content.Shared/MassMedia/Components/SharedNewsWriteComponent.cs b/Content.Shared/MassMedia/Components/SharedNewsWriteComponent.cs index c7386698e0..88261427d8 100644 --- a/Content.Shared/MassMedia/Components/SharedNewsWriteComponent.cs +++ b/Content.Shared/MassMedia/Components/SharedNewsWriteComponent.cs @@ -1,5 +1,5 @@ -using Robust.Shared.Serialization; using Content.Shared.MassMedia.Systems; +using Robust.Shared.Serialization; namespace Content.Shared.MassMedia.Components; diff --git a/Content.Shared/MassMedia/Systems/SharedNewsSystem.cs b/Content.Shared/MassMedia/Systems/SharedNewsSystem.cs index 140452eb65..1021f26734 100644 --- a/Content.Shared/MassMedia/Systems/SharedNewsSystem.cs +++ b/Content.Shared/MassMedia/Systems/SharedNewsSystem.cs @@ -1,3 +1,5 @@ +using Content.Shared.StationRecords; + namespace Content.Shared.MassMedia.Systems; [Serializable] @@ -5,5 +7,7 @@ public struct NewsArticle { public string Name; public string Content; + public string? Author; + public ICollection? AuthorStationRecordKeyIds; public TimeSpan ShareTime; } diff --git a/Content.Shared/PDA/PdaUpdateState.cs b/Content.Shared/PDA/PdaUpdateState.cs index 581db5cad5..ea2569d21e 100644 --- a/Content.Shared/PDA/PdaUpdateState.cs +++ b/Content.Shared/PDA/PdaUpdateState.cs @@ -1,7 +1,6 @@ using Content.Shared.CartridgeLoader; using Robust.Shared.Serialization; - namespace Content.Shared.PDA { [Serializable, NetSerializable] @@ -16,9 +15,14 @@ namespace Content.Shared.PDA public bool HasNewsTab; public string? Address; - public PdaUpdateState(bool flashlightEnabled, bool hasPen, PdaIdInfoText pdaOwnerInfo, - string? stationName, bool hasUplink = false, - bool canPlayMusic = false, bool hasNewsTab = false, string? address = null) + public PdaUpdateState(bool flashlightEnabled, + bool hasPen, + PdaIdInfoText pdaOwnerInfo, + string? stationName, + bool hasUplink = false, + bool canPlayMusic = false, + bool hasNewsTab = false, + string? address = null) { FlashlightEnabled = flashlightEnabled; HasPen = hasPen; diff --git a/Resources/Locale/en-US/mass-media/news-ui.ftl b/Resources/Locale/en-US/mass-media/news-ui.ftl index 9dbc62b402..0b2c528634 100644 --- a/Resources/Locale/en-US/mass-media/news-ui.ftl +++ b/Resources/Locale/en-US/mass-media/news-ui.ftl @@ -5,10 +5,10 @@ news-read-ui-past-text = Past news-read-ui-default-title = Station News news-read-ui-not-found-text = No articles found news-read-ui-time-prefix-text = Publication time: +news-read-ui-no-author = Anonymous news-write-ui-default-title = Mass-media Management news-write-ui-articles-label = Articles: news-write-ui-delete-text = Delete news-write-ui-share-text = Publish news-write-ui-article-name-label = Heading: news-write-ui-article-content-label = Content: -