From 68e1a9cde1342688dc502fa40ba5793a676bed88 Mon Sep 17 00:00:00 2001 From: portfiend <109661617+portfiend@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:15:33 -0400 Subject: [PATCH] AAC tablet for speech-impaired characters (#1491) * basic AAC tablet prototype using station map as a base * set up aac component/system * quick phrase prototype will probably touch this up later * basic example phrases just so i have data to work with * get AACWindow to iterate over quick phrases * add the rest of the job phrases * fix this one job name * actually fix prison guard name * buttons for aac window * fix phrase inheritance * add tabs to aac contaner * fix column spacing and add button padding * aac tablet button colors * AAC tablet sends messages now * add aac tablet voice sound yay * add a 1 second cooldown between phrases * subjects for most departments * location phrases * more phrases * cleanup + sort buttons alphabetically * fix these phrases * even more departmental subject phrases * common phrases * cleanup imports * show name of player that pressed button * aac tablet can be used by multipel people after all it does not rely on state changes and also multiple people can press buttons on a tablet at once * capitalize aac its an acronym * you know what it is its more phrases!!!! * SAFETY PHRASES * last second phrases * redundant phrase * and one more hazard phrase for the road * change voice of aac tablet from borg to alto just sounds nicer * localize ALL Phrases i love utility scripting to automate tedious tasks * add AAC tablet to loadout * add AAC tablet to medfab * tweak: use multiple parents instead of whatever this is * add: justice department phrases * add: time quantity phrases * add: ores and kitchen appliance phrases * fix: resolve duplicate phrases * add: aac tablet sprites * add: justice button style * fix: misplaced this line oops * add: justice dept locations * remove: redundant phrase * re-run tests * fix: move aac tablet loadout format * fix: use Identity instead of Name for aac tablet sender * fix: return on send phrase if id is invalid * fix: remove redundant line * fix: use LocId instead of String for phrase text type * add: new phrases bc upstream updates * fix: newlines * tweak: add end comments to these style comments * fix: this phrase was broken lol --------- Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> --- .../AACTablet/UI/AACBoundUserInterface.cs | 38 ++ .../DeltaV/AACTablet/UI/AACWindow.xaml | 9 + .../DeltaV/AACTablet/UI/AACWindow.xaml.cs | 147 ++++++ Content.Client/Stylesheets/StyleNano.cs | 111 +++++ .../DeltaV/AACTablet/AACTabletComponent.cs | 13 + .../DeltaV/AACTablet/AACTabletSystem.cs | 47 ++ .../DeltaV/AACTablet/SharedAACTabletSystem.cs | 19 + .../QuickPhrase/QuickPhrasePrototype.cs | 51 ++ .../Locale/en-US/deltav/phrases/common.ftl | 92 ++++ .../Locale/en-US/deltav/phrases/species.ftl | 42 ++ .../Locale/en-US/deltav/phrases/subjects.ftl | 195 ++++++++ .../Locale/en-US/deltav/phrases/threats.ftl | 52 +++ .../en-US/deltav/phrases/uncategorized.ftl | 7 + .../Entities/Objects/Devices/aac_tablet.yml | 43 ++ .../Loadouts/Miscellaneous/trinkets.yml | 5 + .../DeltaV/QuickPhrases/Common/commands.yml | 125 +++++ .../DeltaV/QuickPhrases/Common/manners.yml | 75 +++ .../DeltaV/QuickPhrases/Common/numbers.yml | 100 ++++ .../DeltaV/QuickPhrases/Common/pronouns.yml | 65 +++ .../QuickPhrases/Common/qualitative.yml | 70 +++ .../DeltaV/QuickPhrases/Common/questions.yml | 55 +++ .../DeltaV/QuickPhrases/Species/animals.yml | 165 +++++++ .../DeltaV/QuickPhrases/Species/crew.yml | 65 +++ .../QuickPhrases/Species/generic_species.yml | 55 +++ .../DeltaV/QuickPhrases/Subjects/command.yml | 34 ++ .../QuickPhrases/Subjects/engineering.yml | 119 +++++ .../QuickPhrases/Subjects/epistemics.yml | 89 ++++ .../DeltaV/QuickPhrases/Subjects/generic.yml | 230 ++++++++++ .../DeltaV/QuickPhrases/Subjects/justice.yml | 104 +++++ .../QuickPhrases/Subjects/logistics.yml | 156 +++++++ .../DeltaV/QuickPhrases/Subjects/medical.yml | 179 ++++++++ .../DeltaV/QuickPhrases/Subjects/security.yml | 74 +++ .../DeltaV/QuickPhrases/Subjects/service.yml | 144 ++++++ .../DeltaV/QuickPhrases/Threats/hazards.yml | 106 +++++ .../DeltaV/QuickPhrases/Threats/hostiles.yml | 66 +++ .../DeltaV/QuickPhrases/Threats/status.yml | 108 +++++ .../Prototypes/DeltaV/QuickPhrases/base.yml | 86 ++++ .../Prototypes/DeltaV/QuickPhrases/jobs.yml | 309 +++++++++++++ .../DeltaV/QuickPhrases/locations.yml | 434 ++++++++++++++++++ .../DeltaV/Recipes/Lathes/medical.yml | 7 + .../Entities/Structures/Machines/lathe.yml | 1 + .../Prototypes/Loadouts/loadout_groups.yml | 1 + .../Devices/tablets.rsi/aac-inhand-left.png | Bin 0 -> 352 bytes .../Devices/tablets.rsi/aac-inhand-right.png | Bin 0 -> 345 bytes .../tablets.rsi/aac_screen-inhand-left.png | Bin 0 -> 238 bytes .../tablets.rsi/aac_screen-inhand-right.png | Bin 0 -> 238 bytes .../Devices/tablets.rsi/aac_screen.png | Bin 0 -> 268 bytes .../Devices/tablets.rsi/aac_tablet.png | Bin 0 -> 320 bytes .../Objects/Devices/tablets.rsi/meta.json | 33 ++ 49 files changed, 3926 insertions(+) create mode 100644 Content.Client/DeltaV/AACTablet/UI/AACBoundUserInterface.cs create mode 100644 Content.Client/DeltaV/AACTablet/UI/AACWindow.xaml create mode 100644 Content.Client/DeltaV/AACTablet/UI/AACWindow.xaml.cs create mode 100644 Content.Server/DeltaV/AACTablet/AACTabletComponent.cs create mode 100644 Content.Server/DeltaV/AACTablet/AACTabletSystem.cs create mode 100644 Content.Shared/DeltaV/AACTablet/SharedAACTabletSystem.cs create mode 100644 Content.Shared/DeltaV/QuickPhrase/QuickPhrasePrototype.cs create mode 100644 Resources/Locale/en-US/deltav/phrases/common.ftl create mode 100644 Resources/Locale/en-US/deltav/phrases/species.ftl create mode 100644 Resources/Locale/en-US/deltav/phrases/subjects.ftl create mode 100644 Resources/Locale/en-US/deltav/phrases/threats.ftl create mode 100644 Resources/Locale/en-US/deltav/phrases/uncategorized.ftl create mode 100644 Resources/Prototypes/DeltaV/Entities/Objects/Devices/aac_tablet.yml create mode 100644 Resources/Prototypes/DeltaV/Loadouts/Miscellaneous/trinkets.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Common/commands.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Common/manners.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Common/numbers.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Common/pronouns.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Common/qualitative.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Common/questions.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Species/animals.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Species/crew.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Species/generic_species.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Subjects/command.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Subjects/engineering.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Subjects/epistemics.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Subjects/generic.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Subjects/justice.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Subjects/logistics.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Subjects/medical.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Subjects/security.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Subjects/service.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Threats/hazards.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Threats/hostiles.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/Threats/status.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/base.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/jobs.yml create mode 100644 Resources/Prototypes/DeltaV/QuickPhrases/locations.yml create mode 100644 Resources/Prototypes/DeltaV/Recipes/Lathes/medical.yml create mode 100644 Resources/Textures/DeltaV/Objects/Devices/tablets.rsi/aac-inhand-left.png create mode 100644 Resources/Textures/DeltaV/Objects/Devices/tablets.rsi/aac-inhand-right.png create mode 100644 Resources/Textures/DeltaV/Objects/Devices/tablets.rsi/aac_screen-inhand-left.png create mode 100644 Resources/Textures/DeltaV/Objects/Devices/tablets.rsi/aac_screen-inhand-right.png create mode 100644 Resources/Textures/DeltaV/Objects/Devices/tablets.rsi/aac_screen.png create mode 100644 Resources/Textures/DeltaV/Objects/Devices/tablets.rsi/aac_tablet.png create mode 100644 Resources/Textures/DeltaV/Objects/Devices/tablets.rsi/meta.json diff --git a/Content.Client/DeltaV/AACTablet/UI/AACBoundUserInterface.cs b/Content.Client/DeltaV/AACTablet/UI/AACBoundUserInterface.cs new file mode 100644 index 0000000000..6a9330598f --- /dev/null +++ b/Content.Client/DeltaV/AACTablet/UI/AACBoundUserInterface.cs @@ -0,0 +1,38 @@ +using Content.Shared.DeltaV.AACTablet; +using Robust.Shared.Prototypes; + +namespace Content.Client.DeltaV.AACTablet.UI; + +public sealed class AACBoundUserInterface : BoundUserInterface +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + [ViewVariables] + private AACWindow? _window; + + public AACBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + _window?.Close(); + _window = new AACWindow(this, _prototypeManager); + _window.OpenCentered(); + + _window.PhraseButtonPressed += OnPhraseButtonPressed; + _window.OnClose += Close; + } + + private void OnPhraseButtonPressed(string phraseId) + { + SendMessage(new AACTabletSendPhraseMessage(phraseId)); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + _window?.Dispose(); + } +} diff --git a/Content.Client/DeltaV/AACTablet/UI/AACWindow.xaml b/Content.Client/DeltaV/AACTablet/UI/AACWindow.xaml new file mode 100644 index 0000000000..e418393420 --- /dev/null +++ b/Content.Client/DeltaV/AACTablet/UI/AACWindow.xaml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/Content.Client/DeltaV/AACTablet/UI/AACWindow.xaml.cs b/Content.Client/DeltaV/AACTablet/UI/AACWindow.xaml.cs new file mode 100644 index 0000000000..97767affac --- /dev/null +++ b/Content.Client/DeltaV/AACTablet/UI/AACWindow.xaml.cs @@ -0,0 +1,147 @@ +using System.Linq; +using System.Numerics; +using Content.Client.UserInterface.Controls; +using Content.Shared.DeltaV.QuickPhrase; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; + +namespace Content.Client.DeltaV.AACTablet.UI; + +[GenerateTypedNameReferences] +public sealed partial class AACWindow : FancyWindow +{ + private IPrototypeManager _prototypeManager; + public event Action? PhraseButtonPressed; + + public AACWindow(AACBoundUserInterface ui, IPrototypeManager prototypeManager) + { + RobustXamlLoader.Load(this); + _prototypeManager = prototypeManager; + PopulateGui(ui); + } + + private void PopulateGui(AACBoundUserInterface ui) + { + var loc = IoCManager.Resolve(); + var phrases = _prototypeManager.EnumeratePrototypes().ToList(); + + // take ALL phrases and turn them into tabs and groups, so the buttons are sorted and tabbed + var sortedTabs = phrases + .GroupBy(p => p.Tab) + .OrderBy(g => g.Key) + .ToDictionary( + g => g.Key, + g => g.GroupBy(p => p.Group) + .OrderBy(gg => gg.Key) + .ToDictionary( + gg => gg.Key, + gg => gg.OrderBy(p => loc.GetString(p.Text)).ToList() + ) + ); + + var tabContainer = CreateTabContainer(sortedTabs); + WindowBody.AddChild(tabContainer); + } + + private TabContainer CreateTabContainer(Dictionary>> sortedTabs) + { + var tabContainer = new TabContainer(); + var loc = IoCManager.Resolve(); + + foreach (var tab in sortedTabs) + { + var tabName = loc.GetString(tab.Key); + var boxContainer = CreateBoxContainerForTab(tab.Value); + tabContainer.AddChild(boxContainer); + tabContainer.SetTabTitle(tabContainer.ChildCount - 1, tabName); + } + + return tabContainer; + } + + private BoxContainer CreateBoxContainerForTab(Dictionary> groups) + { + var boxContainer = new BoxContainer() + { + HorizontalExpand = true, + Orientation = BoxContainer.LayoutOrientation.Vertical + }; + + foreach (var group in groups) + { + var buttonContainer = CreateButtonContainerForGroup(group.Value); + boxContainer.AddChild(buttonContainer); + } + + return boxContainer; + } + + private GridContainer CreateButtonContainerForGroup(List phrases) + { + var loc = IoCManager.Resolve(); + var buttonContainer = CreateButtonContainer(); + foreach (var phrase in phrases) + { + var text = loc.GetString(phrase.Text); + var button = CreatePhraseButton(text, phrase.StyleClass); + button.OnPressed += _ => OnPhraseButtonPressed(phrase.ID); + buttonContainer.AddChild(button); + } + return buttonContainer; + } + + private static GridContainer CreateButtonContainer() + { + var buttonContainer = new GridContainer + { + Margin = new Thickness(10), + Columns = 4 + }; + + return buttonContainer; + } + + private static Button CreatePhraseButton(string text, string styleClass) + { + var buttonWidth = GetButtonWidth(); + var phraseButton = new Button + { + Access = AccessLevel.Public, + MaxSize = new Vector2(buttonWidth, buttonWidth), + ClipText = false, + HorizontalExpand = true, + StyleClasses = { styleClass } + }; + + var buttonLabel = new RichTextLabel + { + Margin = new Thickness(0, 5), + StyleClasses = { "WhiteText" } + }; + + buttonLabel.SetMessage(text); + phraseButton.AddChild(buttonLabel); + return phraseButton; + } + + private static int GetButtonWidth() + { + var spaceWidth = 10; + var parentWidth = 540; + var columnCount = 4; + + var paddingSize = spaceWidth * 2; + var gutterScale = (columnCount - 1) / columnCount; + var columnWidth = (parentWidth - paddingSize) / columnCount; + var buttonWidth = columnWidth - spaceWidth * gutterScale; + return buttonWidth; + } + + private void OnPhraseButtonPressed(string phraseId) + { + PhraseButtonPressed?.Invoke(phraseId); + } +} diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs index fe3843c34a..0cc40abae0 100644 --- a/Content.Client/Stylesheets/StyleNano.cs +++ b/Content.Client/Stylesheets/StyleNano.cs @@ -152,6 +152,35 @@ namespace Content.Client.Stylesheets public static readonly Color ChatBackgroundColor = Color.FromHex("#25252ADD"); + // DeltaV - AAC button variables + public static readonly string CommandButtonClass = "CommandButton"; + public static readonly string EngineeringButtonClass = "EngineeringButton"; + public static readonly string EpistemicsButtonClass = "EpistemicsButton"; + public static readonly string JusticeButtonClass = "JusticeButton"; + public static readonly string LogisticsButtonClass = "LogisticsButton"; + public static readonly string MedicalButtonClass = "MedicalButton"; + public static readonly string SecurityButtonClass = "SecurityButton"; + public static readonly string ServiceButtonClass = "ServiceButton"; + + // DeltaV - AAC button colors + public static readonly Color CommandButtonColorDefault = Color.FromHex("#404A58"); + public static readonly Color CommandColorHovered = Color.FromHex("#4F587B"); + public static readonly Color EngineeringButtonColorDefault = Color.FromHex("#77684B"); + public static readonly Color EngineeringColorHovered = Color.FromHex("#776D71"); + public static readonly Color EpistemicsButtonColorDefault = Color.FromHex("#6F5973"); + public static readonly Color EpistemicsColorHovered = Color.FromHex("#71638E"); + public static readonly Color LogisticsButtonColorDefault = Color.FromHex("#61503A"); + public static readonly Color LogisticsColorHovered = Color.FromHex("#675C64"); + public static readonly Color JusticeButtonColorDefault = Color.FromHex("#4F3D4C"); + public static readonly Color JusticeColorHovered = Color.FromHex("#5C4B5A"); + public static readonly Color MedicalButtonColorDefault = Color.FromHex("#49687D"); + public static readonly Color MedicalColorHovered = Color.FromHex("#556E95"); + public static readonly Color SecurityButtonColorDefault = Color.FromHex("#724449"); + public static readonly Color SecurityColorHovered = Color.FromHex("#745370"); + public static readonly Color ServiceButtonColorDefault = Color.FromHex("#607952"); + public static readonly Color ServiceColorHovered = Color.FromHex("#667A76"); + // End DeltaV + //Bwoink public const string StyleClassPinButtonPinned = "pinButtonPinned"; public const string StyleClassPinButtonUnpinned = "pinButtonUnpinned"; @@ -1615,6 +1644,88 @@ namespace Content.Client.Stylesheets BackgroundColor = FancyTreeSelectedRowColor, }), + // DeltaV - AAC button styles + Element() + .Class(CommandButtonClass) + .Pseudo(ContainerButton.StylePseudoClassNormal) + .Prop(Control.StylePropertyModulateSelf, CommandButtonColorDefault), + + Element() + .Class(CommandButtonClass) + .Pseudo(ContainerButton.StylePseudoClassHover) + .Prop(Control.StylePropertyModulateSelf, CommandColorHovered), + + Element() + .Class(EngineeringButtonClass) + .Pseudo(ContainerButton.StylePseudoClassNormal) + .Prop(Control.StylePropertyModulateSelf, EngineeringButtonColorDefault), + + Element() + .Class(EngineeringButtonClass) + .Pseudo(ContainerButton.StylePseudoClassHover) + .Prop(Control.StylePropertyModulateSelf, EngineeringColorHovered), + + Element() + .Class(EpistemicsButtonClass) + .Pseudo(ContainerButton.StylePseudoClassNormal) + .Prop(Control.StylePropertyModulateSelf, EpistemicsButtonColorDefault), + + Element() + .Class(EpistemicsButtonClass) + .Pseudo(ContainerButton.StylePseudoClassHover) + .Prop(Control.StylePropertyModulateSelf, EpistemicsColorHovered), + + Element() + .Class(LogisticsButtonClass) + .Pseudo(ContainerButton.StylePseudoClassNormal) + .Prop(Control.StylePropertyModulateSelf, LogisticsButtonColorDefault), + + Element() + .Class(LogisticsButtonClass) + .Pseudo(ContainerButton.StylePseudoClassHover) + .Prop(Control.StylePropertyModulateSelf, LogisticsColorHovered), + + Element() + .Class(MedicalButtonClass) + .Pseudo(ContainerButton.StylePseudoClassNormal) + .Prop(Control.StylePropertyModulateSelf, MedicalButtonColorDefault), + + Element() + .Class(MedicalButtonClass) + .Pseudo(ContainerButton.StylePseudoClassHover) + .Prop(Control.StylePropertyModulateSelf, MedicalColorHovered), + + Element() + .Class(SecurityButtonClass) + .Pseudo(ContainerButton.StylePseudoClassNormal) + .Prop(Control.StylePropertyModulateSelf, SecurityButtonColorDefault), + + Element() + .Class(SecurityButtonClass) + .Pseudo(ContainerButton.StylePseudoClassHover) + .Prop(Control.StylePropertyModulateSelf, SecurityColorHovered), + + Element() + .Class(ServiceButtonClass) + .Pseudo(ContainerButton.StylePseudoClassNormal) + .Prop(Control.StylePropertyModulateSelf, ServiceButtonColorDefault), + + Element() + .Class(ServiceButtonClass) + .Pseudo(ContainerButton.StylePseudoClassHover) + .Prop(Control.StylePropertyModulateSelf, ServiceColorHovered), + + Element() + .Class(JusticeButtonClass) + .Pseudo(ContainerButton.StylePseudoClassNormal) + .Prop(Control.StylePropertyModulateSelf, JusticeButtonColorDefault), + + Element() + .Class(JusticeButtonClass) + .Pseudo(ContainerButton.StylePseudoClassHover) + .Prop(Control.StylePropertyModulateSelf, JusticeColorHovered), + // End DeltaV + // Silicon law edit ui Element