diff --git a/Content.Client/SS220/DialogWindowDescUI/DialogWindowDesc.xaml b/Content.Client/SS220/DialogWindowDescUI/DialogWindowDesc.xaml new file mode 100644 index 00000000000000..e60873ef950d35 --- /dev/null +++ b/Content.Client/SS220/DialogWindowDescUI/DialogWindowDesc.xaml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/Content.Client/SS220/DialogWindowDescUI/DialogWindowDesc.xaml.cs b/Content.Client/SS220/DialogWindowDescUI/DialogWindowDesc.xaml.cs new file mode 100644 index 00000000000000..05f29b4a1e39a1 --- /dev/null +++ b/Content.Client/SS220/DialogWindowDescUI/DialogWindowDesc.xaml.cs @@ -0,0 +1,84 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +using Content.Shared.Administration; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Content.Client.UserInterface.Controls; + +namespace Content.Client.SS220.DialogWindowDescUI; + +/// +/// A modified window in which you can send an additional description to explain a particular action being performed +/// +[GenerateTypedNameReferences] +public sealed partial class DialogWindowDesc : FancyWindow +{ + private List<(string, LineEdit)> _promptLines; + + private bool _finished; + + public Action>? OnConfirmed; + + public Action? OnCancelled; + + public DialogWindowDesc(string title, string dsscEntty, List entries, bool ok = true) + { + RobustXamlLoader.Load(this); + + Title = title; + + OkButton.Visible = ok; + + _promptLines = new(entries.Count); + + for (int i = 0; i < entries.Count; i++) + { + var entry = entries[i]; + + var box = new BoxContainer(); + box.AddChild(new Label() { Text = entry.Prompt, Align = Label.AlignMode.Center, HorizontalExpand = true}); + Prompts.AddChild(box); + + var boxDesc = new BoxContainer(); + boxDesc.AddChild(new Label() { Text = dsscEntty, Align = Label.AlignMode.Center, FontColorOverride = Color.Gray, HorizontalExpand = true }); + Prompts.AddChild(boxDesc); + + var boxEmpty = new BoxContainer(); + boxEmpty.AddChild(new BoxContainer() {MinHeight=20}); + Prompts.AddChild(boxEmpty); + + var boxEdit = new BoxContainer(); + var edit = new LineEdit() { HorizontalExpand = true }; + boxEdit.AddChild(edit); + + _promptLines.Add((entry.FieldId, edit)); + Prompts.AddChild(boxEdit); + } + + OkButton.OnPressed += _ => Confirm(); + + OnClose += () => + { + if (!_finished) + OnCancelled?.Invoke(); + }; + + _promptLines[0].Item2.GrabKeyboardFocus(); + + OpenCentered(); + } + + private void Confirm() + { + var results = new Dictionary(); + foreach (var (field, edit) in _promptLines) + { + results[field] = edit.Text; + } + + _finished = true; + OnConfirmed?.Invoke(results); + Close(); + } +} + diff --git a/Content.Client/SS220/QuickDialog/QuickDialogSystem.cs b/Content.Client/SS220/QuickDialog/QuickDialogSystem.cs new file mode 100644 index 00000000000000..44d9a3e3898033 --- /dev/null +++ b/Content.Client/SS220/QuickDialog/QuickDialogSystem.cs @@ -0,0 +1,35 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +using Content.Client.SS220.DialogWindowDescUI; +using Content.Shared.Administration; + +namespace Content.Client.SS220.QuickDialog; + +public sealed class QuickDialogSystem : EntitySystem +{ + /// + public override void Initialize() + { + SubscribeNetworkEvent(OpenDialog); + } + + private void OpenDialog(QuickDialogDescOpenEvent ev) + { + var ok = (ev.Buttons & QuickDialogButtonFlag.OkButton) != 0; + var window = new DialogWindowDesc(ev.Title, ev.Description, ev.Prompts, ok: ok); + + window.OnConfirmed += responses => + { + RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId, + responses, + QuickDialogButtonFlag.OkButton)); + }; + + window.OnCancelled += () => + { + RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId, + new(), + QuickDialogButtonFlag.CancelButton)); + }; + } +} + diff --git a/Content.Server/Administration/QuickDialogSystem.OpenDialog.cs b/Content.Server/Administration/QuickDialogSystem.OpenDialog.cs index 1bbc8a6c9e8710..399a937a9bb20d 100644 --- a/Content.Server/Administration/QuickDialogSystem.OpenDialog.cs +++ b/Content.Server/Administration/QuickDialogSystem.OpenDialog.cs @@ -173,4 +173,33 @@ public void OpenDialog(ICommonSession session, string title, str cancelAction ?? (() => { }) ); } + + //SS220-RenameStart - start + [PublicAPI] + public void OpenDialog(ICommonSession session, string title, string description, string prompt, Action okAction, + Action? cancelAction = null) + { + OpenDialogInternal( + session, + title, + description, + new List + { + new("1", TypeToEntryType(typeof(T1)), prompt) + }, + QuickDialogButtonFlag.OkButton | QuickDialogButtonFlag.CancelButton, + (ev => + { + if (TryParseQuickDialog(TypeToEntryType(typeof(T1)), ev.Responses["1"], out var v1)) + okAction.Invoke(v1); + else + { + session.Channel.Disconnect("Replied with invalid quick dialog data."); + cancelAction?.Invoke(); + } + }), + cancelAction ?? (() => { }) + ); + } + //SS220-RenameStart - end } diff --git a/Content.Server/Administration/QuickDialogSystem.cs b/Content.Server/Administration/QuickDialogSystem.cs index df2953f98d19b0..d149e7e9b22b4d 100644 --- a/Content.Server/Administration/QuickDialogSystem.cs +++ b/Content.Server/Administration/QuickDialogSystem.cs @@ -103,6 +103,28 @@ private void OpenDialogInternal(ICommonSession session, string title, List entries, QuickDialogButtonFlag buttons, Action okAction, Action cancelAction) + { + var did = GetDialogId(); + RaiseNetworkEvent( + new QuickDialogDescOpenEvent( + title, + description, + entries, + did, + buttons), + session + ); + + _openDialogs.Add(did, (okAction, cancelAction)); + if (!_openDialogsByUser.ContainsKey(session.UserId)) + _openDialogsByUser.Add(session.UserId, new List()); + + _openDialogsByUser[session.UserId].Add(did); + } + //SS220-RenameStart - end + private bool TryParseQuickDialog(QuickDialogEntryType entryType, string input, [NotNullWhen(true)] out T? output) { switch (entryType) diff --git a/Content.Server/SS220/RenameStart/RenameStartComponent.cs b/Content.Server/SS220/RenameStart/RenameStartComponent.cs new file mode 100644 index 00000000000000..b94ce6e94927f5 --- /dev/null +++ b/Content.Server/SS220/RenameStart/RenameStartComponent.cs @@ -0,0 +1,17 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +using Content.Shared.Preferences; + +namespace Content.Server.SS220.RenameStart; + +/// +/// This is used for change the entity name once the player starts controlling +/// +[RegisterComponent] +public sealed partial class RenameStartComponent : Component +{ + [DataField] + public int MinChar = 2; + + [DataField] + public int MaxChar = HumanoidCharacterProfile.MaxNameLength; +} diff --git a/Content.Server/SS220/RenameStart/RenameStartSystem.cs b/Content.Server/SS220/RenameStart/RenameStartSystem.cs new file mode 100644 index 00000000000000..b09f76a0bef94a --- /dev/null +++ b/Content.Server/SS220/RenameStart/RenameStartSystem.cs @@ -0,0 +1,90 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +using System.Text.RegularExpressions; +using Content.Server.Administration; +using Content.Server.Administration.Systems; +using Content.Shared.Administration; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; +using Robust.Shared.Player; + +namespace Content.Server.SS220.RenameStart; + +/// +/// This handles opens the ui to change your name at the beginning of the game. Renaming is necessary for such roles as a clown with a “custom” name +/// +public sealed class RenameStartSystem : EntitySystem +{ + [Dependency] private readonly QuickDialogSystem _quickDialog = default!; + [Dependency] private readonly MetaDataSystem _meta = default!; + [Dependency] private readonly AdminFrozenSystem _frozen = default!; + private static readonly Regex Expressions = new("[^А-Яа-яёЁ0-9' \\-?!,.]"); + /// + public override void Initialize() + { + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnRemoveComponent); + } + + private void OnPlayerAttached(Entity ent, ref PlayerAttachedEvent args) + { + _frozen.FreezeAndMute(ent.Owner); //prevent players from changing their name after showing up with their initial name + + ChangeName(ent.Owner); + } + + private void ChangeName(EntityUid entOwner) + { + if(!TryComp(entOwner, out var actorComp)) + { + RemComp(entOwner); + return; + } + + if(!TryComp(entOwner, out var renameComp)) + { + RemComp(entOwner); + return; + } + + _quickDialog.OpenDialog(actorComp.PlayerSession, + Loc.GetString("rename-window-title"), + description: Loc.GetString("rename-window-desc"), + Loc.GetString("rename-window-promt"), + (LongString newName) => + { + if (newName.String.Length < renameComp.MinChar || + newName.String.Length > renameComp.MaxChar || + Expressions.IsMatch(newName.String)) + { + ChangeName(entOwner); + return; + } + + if(!TryComp(entOwner, out var mindContComp)) + { + RemComp(entOwner); + return; + } + + if (!TryComp(mindContComp.Mind, out var mindComp)) + { + RemComp(entOwner); + return; + } + + mindComp.CharacterName = newName.String; + + _meta.SetEntityName(entOwner, newName); + + RemComp(entOwner); + }, () => + { + RemComp(entOwner); + }); + } + + private void OnRemoveComponent(Entity ent, ref ComponentShutdown args) + { + RemComp(ent.Owner); + } +} diff --git a/Content.Shared/Administration/QuickDialogOpenEvent.cs b/Content.Shared/Administration/QuickDialogOpenEvent.cs index f4e77ab9990adb..01388e41bcff87 100644 --- a/Content.Shared/Administration/QuickDialogOpenEvent.cs +++ b/Content.Shared/Administration/QuickDialogOpenEvent.cs @@ -37,6 +37,46 @@ public QuickDialogOpenEvent(string title, List prompts, int di } } +//SS220-RenameStart - start +[Serializable, NetSerializable] +public sealed class QuickDialogDescOpenEvent : EntityEventArgs +{ + /// + /// The title of the dialog. + /// + public string Title; + + /// + /// The title of the dialog. + /// + public string Description; + + /// + /// The internal dialog ID. + /// + public int DialogId; + + /// + /// The prompts to show the user. + /// + public List Prompts; + + /// + /// The buttons presented for the user. + /// + public QuickDialogButtonFlag Buttons = QuickDialogButtonFlag.OkButton | QuickDialogButtonFlag.CancelButton; + + public QuickDialogDescOpenEvent(string title, string description, List prompts, int dialogId, QuickDialogButtonFlag buttons) + { + Title = title; + Description = description; + Prompts = prompts; + Buttons = buttons; + DialogId = dialogId; + } +} +//SS220-RenameStart - end + /// /// A networked event raised when the client replies to a quick dialog. /// diff --git a/Resources/Locale/ru-RU/ss220/quick-dialog/quick-dialog.ftl b/Resources/Locale/ru-RU/ss220/quick-dialog/quick-dialog.ftl new file mode 100644 index 00000000000000..094b56626380d2 --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/quick-dialog/quick-dialog.ftl @@ -0,0 +1 @@ +quick-dialog-ui-confirm = Подтвердить \ No newline at end of file diff --git a/Resources/Locale/ru-RU/ss220/rename-start-window/rename-start-window.ftl b/Resources/Locale/ru-RU/ss220/rename-start-window/rename-start-window.ftl new file mode 100644 index 00000000000000..f81c09034112f6 --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/rename-start-window/rename-start-window.ftl @@ -0,0 +1,7 @@ +rename-window-title = Переименование +rename-window-promt = Выберите свое новое имя +rename-window-desc = + Ваше имя не должно нарушать 4 пункт правил: + Ваш персонаж - находится на передовой космической станции. + Клоуну и миму предоставляется более широкое поле для мемных имён + Имена синтетиков так же имеют свою специфику. Ознакомьтесь с ними на вики. \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml b/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml index c84f9079e566e9..2b05a04f3b93b6 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml @@ -18,6 +18,7 @@ special: - !type:AddComponentSpecial components: + - type: RenameStart #SS220-RenameStart - type: Clumsy clumsyDamage: types: #literally just picked semi random valus. i tested this once and tweaked it. diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml b/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml index 4eb4e8151ce258..03b7cf756dba23 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml @@ -20,6 +20,7 @@ components: - type: MimePowers - type: FrenchAccent + - type: RenameStart #SS220-RenameStart - type: startingGear id: MimeGear diff --git a/Resources/Prototypes/Roles/Jobs/Science/borg.yml b/Resources/Prototypes/Roles/Jobs/Science/borg.yml index f3b3b6dff5ddf2..01f30536b7105a 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/borg.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/borg.yml @@ -18,6 +18,10 @@ supervisors: job-supervisors-rd jobEntity: StationAiBrain applyTraits: false + special: #SS220-RenameStart - start + - !type:AddComponentSpecial + components: + - type: RenameStart #SS220-RenameStart - end - type: job id: Borg @@ -32,3 +36,7 @@ supervisors: job-supervisors-rd jobEntity: PlayerBorgGeneric applyTraits: false + special: #SS220-RenameStart - start + - !type:AddComponentSpecial + components: + - type: RenameStart #SS220-RenameStart - end \ No newline at end of file