diff --git a/Content.Client/Store/Ui/StoreBoundUserInterface.cs b/Content.Client/Store/Ui/StoreBoundUserInterface.cs index 2301d11248..8ff5ff1ca8 100644 --- a/Content.Client/Store/Ui/StoreBoundUserInterface.cs +++ b/Content.Client/Store/Ui/StoreBoundUserInterface.cs @@ -53,11 +53,14 @@ public sealed class StoreBoundUserInterface : BoundUserInterface _menu.UpdateBalance(msg.Balance); _menu.PopulateStoreCategoryButtons(msg.Listings); _menu.UpdateListing(msg.Listings.ToList()); + _menu.SetFooterVisibility(msg.ShowFooter); break; case StoreInitializeState msg: _windowName = msg.Name; if (_menu != null && _menu.Window != null) + { _menu.Window.Title = msg.Name; + } break; } } diff --git a/Content.Client/Store/Ui/StoreMenu.xaml b/Content.Client/Store/Ui/StoreMenu.xaml index fbbf6c1343..824c153671 100644 --- a/Content.Client/Store/Ui/StoreMenu.xaml +++ b/Content.Client/Store/Ui/StoreMenu.xaml @@ -50,5 +50,15 @@ + + + + + + + diff --git a/Content.Client/Store/Ui/StoreMenu.xaml.cs b/Content.Client/Store/Ui/StoreMenu.xaml.cs index f39101ed16..a3f246badb 100644 --- a/Content.Client/Store/Ui/StoreMenu.xaml.cs +++ b/Content.Client/Store/Ui/StoreMenu.xaml.cs @@ -77,6 +77,11 @@ public sealed partial class StoreMenu : DefaultWindow } } + public void SetFooterVisibility(bool visible) + { + TraitorFooter.Visible = visible; + } + private void OnWithdrawButtonDown(BaseButton.ButtonEventArgs args) { // check if window is already open diff --git a/Content.Server/Mind/Commands/RenameCommand.cs b/Content.Server/Mind/Commands/RenameCommand.cs index 7efe3d9b4f..0f92ae7c29 100644 --- a/Content.Server/Mind/Commands/RenameCommand.cs +++ b/Content.Server/Mind/Commands/RenameCommand.cs @@ -82,11 +82,13 @@ public sealed class RenameCommand : IConsoleCommand // PDAs if (entSysMan.TryGetEntitySystem(out var pdaSystem)) { - foreach (var pdaComponent in entMan.EntityQuery()) + var query = entMan.EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var pda)) { - if (pdaComponent.OwnerName != oldName) - continue; - pdaSystem.SetOwner(pdaComponent, name); + if (pda.OwnerName == oldName) + { + pdaSystem.SetOwner(uid, pda, name); + } } } diff --git a/Content.Server/PDA/PDASystem.cs b/Content.Server/PDA/PDASystem.cs index 8362142c83..92bacf95b7 100644 --- a/Content.Server/PDA/PDASystem.cs +++ b/Content.Server/PDA/PDASystem.cs @@ -6,6 +6,8 @@ using Content.Server.Light.EntitySystems; using Content.Server.Light.Events; using Content.Server.PDA.Ringer; using Content.Server.Station.Systems; +using Content.Server.Store.Components; +using Content.Server.Store.Systems; using Content.Server.UserInterface; using Content.Shared.PDA; using Robust.Server.GameObjects; @@ -18,12 +20,13 @@ namespace Content.Server.PDA { public sealed class PDASystem : SharedPDASystem { + [Dependency] private readonly CartridgeLoaderSystem _cartridgeLoader = default!; + [Dependency] private readonly InstrumentSystem _instrument = default!; + [Dependency] private readonly RingerSystem _ringer = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly StoreSystem _store = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly UnpoweredFlashlightSystem _unpoweredFlashlight = default!; - [Dependency] private readonly RingerSystem _ringerSystem = default!; - [Dependency] private readonly InstrumentSystem _instrumentSystem = default!; - [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; - [Dependency] private readonly StationSystem _stationSystem = default!; - [Dependency] private readonly CartridgeLoaderSystem _cartridgeLoaderSystem = default!; public override void Initialize() { @@ -40,43 +43,46 @@ namespace Content.Server.PDA if (!TryComp(uid, out ServerUserInterfaceComponent? uiComponent)) return; - UpdateStationName(pda); + UpdateStationName(uid, pda); - if (_uiSystem.TryGetUi(uid, PDAUiKey.Key, out var ui, uiComponent)) + if (_ui.TryGetUi(uid, PDAUiKey.Key, out var ui, uiComponent)) ui.OnReceiveMessage += (msg) => OnUIMessage(pda, msg); } protected override void OnItemInserted(EntityUid uid, PDAComponent pda, EntInsertedIntoContainerMessage args) { base.OnItemInserted(uid, pda, args); - UpdatePDAUserInterface(pda); + UpdatePdaUi(uid, pda); } protected override void OnItemRemoved(EntityUid uid, PDAComponent pda, EntRemovedFromContainerMessage args) { base.OnItemRemoved(uid, pda, args); - UpdatePDAUserInterface(pda); + UpdatePdaUi(uid, pda); } private void OnLightToggle(EntityUid uid, PDAComponent pda, LightToggleEvent args) { pda.FlashlightOn = args.IsOn; - UpdatePDAUserInterface(pda); + UpdatePdaUi(uid, pda); } - public void SetOwner(PDAComponent pda, string ownerName) + public void SetOwner(EntityUid uid, PDAComponent pda, string ownerName) { pda.OwnerName = ownerName; - UpdatePDAUserInterface(pda); + UpdatePdaUi(uid, pda); } private void OnGridChanged(EntityUid uid, PDAComponent pda, GridModifiedEvent args) { - UpdateStationName(pda); - UpdatePDAUserInterface(pda); + UpdateStationName(uid, pda); + UpdatePdaUi(uid, pda); } - private void UpdatePDAUserInterface(PDAComponent pda) + /// + /// Send new UI state to clients, call if you modify something like uplink. + /// + public void UpdatePdaUi(EntityUid uid, PDAComponent pda) { var ownerInfo = new PDAIdInfoText { @@ -85,49 +91,62 @@ namespace Content.Server.PDA JobTitle = pda.ContainedID?.JobTitle }; - if (!_uiSystem.TryGetUi(pda.Owner, PDAUiKey.Key, out var ui)) + if (!_ui.TryGetUi(uid, PDAUiKey.Key, out var ui)) return; - var address = GetDeviceNetAddress(pda.Owner); - var hasInstrument = HasComp(pda.Owner); - var state = new PDAUpdateState(pda.FlashlightOn, pda.PenSlot.HasItem, ownerInfo, pda.StationName, false, hasInstrument, address); + var address = GetDeviceNetAddress(uid); + var hasInstrument = HasComp(uid); + var showUplink = HasComp(uid) && IsUnlocked(uid); - _cartridgeLoaderSystem?.UpdateUiState(pda.Owner, state); + var state = new PDAUpdateState(pda.FlashlightOn, pda.PenSlot.HasItem, ownerInfo, pda.StationName, showUplink, hasInstrument, address); + _cartridgeLoader?.UpdateUiState(uid, state); } private void OnUIMessage(PDAComponent pda, ServerBoundUserInterfaceMessage msg) { - var pdaEnt = pda.Owner; + var uid = pda.Owner; // todo: move this to entity events switch (msg.Message) { case PDARequestUpdateInterfaceMessage _: - UpdatePDAUserInterface(pda); + UpdatePdaUi(uid, pda); break; case PDAToggleFlashlightMessage _: { - if (EntityManager.TryGetComponent(pdaEnt, out UnpoweredFlashlightComponent? flashlight)) - _unpoweredFlashlight.ToggleLight(pdaEnt, flashlight); + if (TryComp(uid, out var flashlight)) + _unpoweredFlashlight.ToggleLight(uid, flashlight); break; } case PDAShowRingtoneMessage _: { - if (EntityManager.TryGetComponent(pdaEnt, out RingerComponent? ringer)) - _ringerSystem.ToggleRingerUI(ringer, msg.Session); + if (TryComp(uid, out var ringer)) + _ringer.ToggleRingerUI(ringer, msg.Session); break; } case PDAShowMusicMessage _: { - if (TryComp(pdaEnt, out InstrumentComponent? instrument)) - _instrumentSystem.ToggleInstrumentUi(pdaEnt, msg.Session, instrument); + if (TryComp(uid, out var instrument)) + _instrument.ToggleInstrumentUi(uid, msg.Session, instrument); + break; + } + case PDAShowUplinkMessage _: + { + // check if its locked again to prevent malicious clients opening locked uplinks + if (TryComp(uid, out var store) && IsUnlocked(uid)) + _store.ToggleUi(msg.Session.AttachedEntity!.Value, uid, store); break; } } } - private void UpdateStationName(PDAComponent pda) + private bool IsUnlocked(EntityUid uid) { - var station = _stationSystem.GetOwningStation(pda.Owner); + return TryComp(uid, out var uplink) ? uplink.Unlocked : true; + } + + private void UpdateStationName(EntityUid uid, PDAComponent pda) + { + var station = _station.GetOwningStation(uid); pda.StationName = station is null ? null : Name(station.Value); } diff --git a/Content.Server/PDA/Ringer/RingerSystem.cs b/Content.Server/PDA/Ringer/RingerSystem.cs index b0b56bc740..a75dcce74d 100644 --- a/Content.Server/PDA/Ringer/RingerSystem.cs +++ b/Content.Server/PDA/Ringer/RingerSystem.cs @@ -3,6 +3,8 @@ using Content.Server.Store.Systems; using Content.Server.UserInterface; using Content.Shared.PDA; using Content.Shared.PDA.Ringer; +using Content.Shared.Store; +using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Audio; using Robust.Shared.Player; @@ -14,8 +16,10 @@ namespace Content.Server.PDA.Ringer { public sealed class RingerSystem : SharedRingerSystem { + [Dependency] private readonly PDASystem _pda = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly StoreSystem _store = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; public override void Initialize() { @@ -26,7 +30,7 @@ namespace Content.Server.PDA.Ringer SubscribeLocalEvent(RandomizeUplinkCode); // RingerBoundUserInterface Subscriptions SubscribeLocalEvent(OnSetRingtone); - SubscribeLocalEvent(OnSetUplinkRingtone); + SubscribeLocalEvent(OnSetUplinkRingtone); SubscribeLocalEvent(RingerPlayRingtone); SubscribeLocalEvent(UpdateRingerUserInterfaceDriver); @@ -50,17 +54,28 @@ namespace Content.Server.PDA.Ringer // Client sent us an updated ringtone so set it to that. if (args.Ringtone.Length != RingtoneLength) return; + var ev = new BeforeRingtoneSetEvent(args.Ringtone); + RaiseLocalEvent(uid, ref ev); + if (ev.Handled) + return; + UpdateRingerRingtone(ringer, args.Ringtone); } - private void OnSetUplinkRingtone(EntityUid uid, RingerUplinkComponent uplink, RingerSetRingtoneMessage args) + private void OnSetUplinkRingtone(EntityUid uid, RingerUplinkComponent uplink, ref BeforeRingtoneSetEvent args) { - if (uplink.Code.SequenceEqual(args.Ringtone) && - args.Session.AttachedEntity != null && - TryComp(uid, out var store)) + if (uplink.Code.SequenceEqual(args.Ringtone) && TryComp(uid, out var store)) { - var user = args.Session.AttachedEntity.Value; - _store.ToggleUi(args.Session.AttachedEntity.Value, uid, store); + uplink.Unlocked = !uplink.Unlocked; + if (TryComp(uid, out var pda)) + _pda.UpdatePdaUi(uid, pda); + + // can't keep store open after locking it + if (!uplink.Unlocked) + _ui.TryCloseAll(uid, StoreUiKey.Key); + + // no saving the code to prevent meta click set on sus guys pda -> wewlad + args.Handled = true; } } @@ -161,3 +176,6 @@ namespace Content.Server.PDA.Ringer } } } + +[ByRefEvent] +public record struct BeforeRingtoneSetEvent(Note[] Ringtone, bool Handled = false); diff --git a/Content.Server/PDA/Ringer/RingerUplinkComponent.cs b/Content.Server/PDA/Ringer/RingerUplinkComponent.cs index 138c48fb22..a446468ee1 100644 --- a/Content.Server/PDA/Ringer/RingerUplinkComponent.cs +++ b/Content.Server/PDA/Ringer/RingerUplinkComponent.cs @@ -10,9 +10,15 @@ namespace Content.Server.PDA.Ringer; public sealed class RingerUplinkComponent : Component { /// - /// Notes to set ringtone to in order to open the uplink. + /// Notes to set ringtone to in order to lock or unlock the uplink. /// Automatically initialized to random notes. /// [DataField("code")] public Note[] Code = new Note[RingerSystem.RingtoneLength]; + + /// + /// Whether to show the toggle uplink button in pda settings. + /// + [DataField("unlocked"), ViewVariables(VVAccess.ReadWrite)] + public bool Unlocked; } diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index d8fece6575..fcd61ed066 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -231,7 +231,7 @@ public sealed class StationSpawningSystem : EntitySystem _accessSystem.SetAccessToJob(cardId, jobPrototype, extendedAccess); - _pdaSystem.SetOwner(pdaComponent, characterName); + _pdaSystem.SetOwner(idUid.Value, pdaComponent, characterName); } diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index 2ad246c290..cde624ef72 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -1,5 +1,6 @@ using Content.Server.Actions; using Content.Server.Administration.Logs; +using Content.Server.PDA.Ringer; using Content.Server.Stack; using Content.Server.Store.Components; using Content.Server.UserInterface; @@ -49,6 +50,17 @@ public sealed partial class StoreSystem UpdateUserInterface(user, storeEnt, component); } + /// + /// Closes the store UI for everyone, if it's open + /// + public void CloseUi(EntityUid uid, StoreComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + _ui.TryCloseAll(uid, StoreUiKey.Key); + } + /// /// Updates the user interface for a store and refreshes the listings /// @@ -83,7 +95,9 @@ public sealed partial class StoreSystem // TODO: if multiple users are supposed to be able to interact with a single BUI & see different // stores/listings, this needs to use session specific BUI states. - var state = new StoreUpdateState(component.LastAvailableListings, allCurrency); + // only tell operatives to lock their uplink if it can be locked + var showFooter = HasComp(store); + var state = new StoreUpdateState(component.LastAvailableListings, allCurrency, showFooter); _ui.SetUiState(ui, state); } diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index d596f6ac4c..e4a4410e22 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Mind.Components; +using Content.Server.PDA.Ringer; using Content.Server.Store.Components; using Content.Server.UserInterface; using Content.Shared.FixedPoint; @@ -72,9 +73,9 @@ public sealed partial class StoreSystem : EntitySystem if (args.Target == null || !TryComp(args.Target, out var store)) return; - // require the store to be open before inserting currency + // if the store can be locked, it must be unlocked first before inserting currency var user = args.User; - if (!TryComp(user, out var actor) || !_ui.SessionHasOpenUi(uid, StoreUiKey.Key, actor.PlayerSession)) + if (TryComp(args.Target, out var uplink) && !uplink.Unlocked) return; args.Handled = TryAddCurrency(GetCurrencyValue(uid, component), args.Target.Value, store); @@ -183,6 +184,8 @@ public sealed partial class StoreSystem : EntitySystem var ui = _ui.GetUiOrNull(uid, StoreUiKey.Key); if (ui != null) + { _ui.SetUiState(ui, new StoreInitializeState(preset.StoreName)); + } } } diff --git a/Content.Shared/Store/StoreUi.cs b/Content.Shared/Store/StoreUi.cs index 6e3838d368..a142cf4e4f 100644 --- a/Content.Shared/Store/StoreUi.cs +++ b/Content.Shared/Store/StoreUi.cs @@ -16,10 +16,13 @@ public sealed class StoreUpdateState : BoundUserInterfaceState public readonly Dictionary Balance; - public StoreUpdateState(HashSet listings, Dictionary balance) + public readonly bool ShowFooter; + + public StoreUpdateState(HashSet listings, Dictionary balance, bool showFooter) { Listings = listings; Balance = balance; + ShowFooter = showFooter; } } diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl index 872636165b..5ce0d5be1f 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl @@ -53,7 +53,7 @@ traitor-role-codewords = Listen for them, and keep them secret. traitor-role-uplink-code = Set your ringtone to the notes {$code} to lock or unlock your uplink. - Remember to lock it and change it, or the stations crew will easily open it too! + Remember to lock it after, or the stations crew will easily open it too! # don't need all the flavour text for character menu traitor-role-codewords-short = diff --git a/Resources/Locale/en-US/store/store.ftl b/Resources/Locale/en-US/store/store.ftl index 96778ef47f..7af2b05533 100644 --- a/Resources/Locale/en-US/store/store.ftl +++ b/Resources/Locale/en-US/store/store.ftl @@ -2,5 +2,7 @@ store-ui-default-title = Store store-ui-default-withdraw-text = Withdraw store-ui-balance-display = {$currency}: {$amount} store-ui-price-display = {$amount} {$currency} +store-ui-traitor-flavor = Copyright (C) NT -30643 +store-ui-traitor-warning = Operatives must lock their uplinks after use to avoid detection. -store-withdraw-button-ui = Withdraw {$currency} \ No newline at end of file +store-withdraw-button-ui = Withdraw {$currency}