diff --git a/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs b/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs index e69f577407..3a2b469522 100644 --- a/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs +++ b/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Client._DV.CustomObjectiveSummary; // DeltaV using Content.Client.CharacterInfo; using Content.Client.Gameplay; using Content.Client.Stylesheets; @@ -31,6 +32,7 @@ public sealed class CharacterUIController : UIController, IOnStateEntered 0) + { + var button = new Button + { + Text = Loc.GetString("custom-objective-button-text"), + Margin = new Thickness(0, 10, 0, 10) + }; + button.OnPressed += _ => _objective.OpenWindow(); + + _window.Objectives.AddChild(button); + } + // End DeltaV Additions if (briefing != null) { diff --git a/Content.Client/_DV/CustomObjectiveSummary/CustomObjectiveSummaryUIController.cs b/Content.Client/_DV/CustomObjectiveSummary/CustomObjectiveSummaryUIController.cs new file mode 100644 index 0000000000..3e36d6a28a --- /dev/null +++ b/Content.Client/_DV/CustomObjectiveSummary/CustomObjectiveSummaryUIController.cs @@ -0,0 +1,44 @@ +using Content.Shared._DV.CustomObjectiveSummary; +using Robust.Client.UserInterface.Controllers; +using Robust.Shared.Network; + +namespace Content.Client._DV.CustomObjectiveSummary; + +public sealed class CustomObjectiveSummaryUIController : UIController +{ + [Dependency] private readonly IClientNetManager _net = default!; + + private CustomObjectiveSummaryWindow? _window; + + public override void Initialize() + { + base.Initialize(); + SubscribeNetworkEvent(OnCustomObjectiveSummaryOpen); + } + + private void OnCustomObjectiveSummaryOpen(CustomObjectiveSummaryOpenMessage msg, EntitySessionEventArgs args) + { + OpenWindow(); + } + + public void OpenWindow() + { + // If a window is already open, close it + _window?.Close(); + + _window = new CustomObjectiveSummaryWindow(); + _window.OpenCentered(); + _window.OnClose += () => _window = null; + _window.OnSubmitted += OnFeedbackSubmitted; + } + + private void OnFeedbackSubmitted(string args) + { + var msg = new CustomObjectiveClientSetObjective + { + Summary = args, + }; + _net.ClientSendMessage(msg); + _window?.Close(); + } +} diff --git a/Content.Client/_DV/CustomObjectiveSummary/CustomObjectiveSummaryWindow.xaml b/Content.Client/_DV/CustomObjectiveSummary/CustomObjectiveSummaryWindow.xaml new file mode 100644 index 0000000000..1e28d96a02 --- /dev/null +++ b/Content.Client/_DV/CustomObjectiveSummary/CustomObjectiveSummaryWindow.xaml @@ -0,0 +1,13 @@ + + + + diff --git a/Content.Client/_DV/CustomObjectiveSummary/CustomObjectiveSummaryWindow.xaml.cs b/Content.Client/_DV/CustomObjectiveSummary/CustomObjectiveSummaryWindow.xaml.cs new file mode 100644 index 0000000000..609893618c --- /dev/null +++ b/Content.Client/_DV/CustomObjectiveSummary/CustomObjectiveSummaryWindow.xaml.cs @@ -0,0 +1,49 @@ +using Content.Client.UserInterface.Controls; +using Content.Shared.Mind; +using Robust.Client.AutoGenerated; +using Robust.Client.Player; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Utility; + +namespace Content.Client._DV.CustomObjectiveSummary; + +[GenerateTypedNameReferences] +public sealed partial class CustomObjectiveSummaryWindow : FancyWindow +{ + [Dependency] private readonly IPlayerManager _players = default!; + [Dependency] private readonly IEntityManager _entity = default!; + + private SharedMindSystem? _mind; + + private readonly int _maxLength = 256; + + public event Action? OnSubmitted; + + public CustomObjectiveSummaryWindow() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + SubmitButton.OnPressed += + _ => OnSubmitted?.Invoke(Rope.Collapse(ObjectiveSummaryTextEdit.TextRope)); + ObjectiveSummaryTextEdit.OnTextChanged += _ => UpdateWordCount(); + + _mind ??= _entity.System(); + + _mind.TryGetMind(_players.LocalSession, out var mindUid, out _); + + UpdateWordCount(); + + if (_entity.TryGetComponent(mindUid, out var summary)) + ObjectiveSummaryTextEdit.TextRope = new Rope.Leaf(summary.ObjectiveSummary); + + UpdateWordCount(); + } + + private void UpdateWordCount() + { + // Disable the button if its over the max length.sa + SubmitButton.Disabled = ObjectiveSummaryTextEdit.TextLength > _maxLength; + CharacterLimitLabel.Text = ObjectiveSummaryTextEdit.TextLength + "/" + _maxLength; + } +} diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index 73bb74d524..b863ea74ca 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -12,6 +12,7 @@ using Robust.Shared.Random; using System.Linq; using System.Text; using Content.Server.Objectives.Commands; +using Content.Shared._DV.CustomObjectiveSummary; // DeltaV using Content.Shared.Prototypes; using Content.Shared.Roles.Jobs; using Robust.Server.Player; @@ -162,6 +163,7 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem totalObjectives++; agentSummary.Append("- "); + /* Begin DeltaV removal - Removed greentext if (progress > 0.99f) { agentSummary.AppendLine(Loc.GetString( @@ -180,10 +182,39 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem ("markupColor", "red") )); } + End DeltaV removal */ + // Begin DeltaV Additions - Generic objective + agentSummary.AppendLine(Loc.GetString( + "objectives-objective", + ("objective", objectiveTitle) + )); + // End DeltaV Additions } } var successRate = totalObjectives > 0 ? (float) completedObjectives / totalObjectives : 0f; + // Begin DeltaV Additions - custom objective response. + if (TryComp(mindId, out var customComp)) + { + // We have to spit it like this to make it readable. Yeah, it sucks but for some reason the entire thing + // is just one long string... + var words = customComp.ObjectiveSummary.Split(" "); + var currentLine = ""; + foreach (var word in words) + { + currentLine += word + " "; + + // magic number + if (currentLine.Length <= 50) + continue; + + agentSummary.AppendLine(Loc.GetString("custom-objective-format", ("line", currentLine))); + currentLine = ""; + } + + agentSummary.AppendLine(Loc.GetString("custom-objective-format", ("line", currentLine))); + } + // End DeltaV Additions agentSummaries.Add((agentSummary.ToString(), successRate, completedObjectives)); } diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index afa77421bd..f7551fca44 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -18,6 +18,7 @@ using Content.Server.Shuttles.Events; using Content.Server.Station.Components; using Content.Server.Station.Events; using Content.Server.Station.Systems; +using Content.Shared._DV.CustomObjectiveSummary; // DeltaV using Content.Shared.Access.Systems; using Content.Shared.CCVar; using Content.Shared.Database; @@ -219,6 +220,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem }; _deviceNetworkSystem.QueuePacket(uid, null, payload, netComp.TransmitFrequency); } + RaiseLocalEvent(new EvacShuttleLeftEvent()); // DeltaV } /// diff --git a/Content.Server/_DV/CustomObjectiveSummary/CustomObjectiveSummarySystem.cs b/Content.Server/_DV/CustomObjectiveSummary/CustomObjectiveSummarySystem.cs new file mode 100644 index 0000000000..5debc7f977 --- /dev/null +++ b/Content.Server/_DV/CustomObjectiveSummary/CustomObjectiveSummarySystem.cs @@ -0,0 +1,72 @@ +using Content.Server.Administration.Logs; +using Content.Shared._DV.CustomObjectiveSummary; +using Content.Shared._DV.FeedbackOverwatch; +using Content.Shared.Database; +using Content.Shared.GameTicking; +using Content.Shared.Mind; +using Robust.Shared.Network; + +namespace Content.Server._DV.CustomObjectiveSummary; + +public sealed class CustomObjectiveSummarySystem : EntitySystem +{ + [Dependency] private readonly IServerNetManager _net = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly IAdminLogManager _adminLog = default!; + [Dependency] private readonly SharedFeedbackOverwatchSystem _feedback = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnEvacShuttleLeft); + SubscribeLocalEvent(OnRoundEnd); + + _net.RegisterNetMessage(OnCustomObjectiveFeedback); + } + + private void OnCustomObjectiveFeedback(CustomObjectiveClientSetObjective msg) + { + if (!_mind.TryGetMind(msg.MsgChannel.UserId, out var mind)) + return; + + if (mind.Value.Comp.Objectives.Count == 0) + return; + + var comp = EnsureComp(mind.Value); + + comp.ObjectiveSummary = msg.Summary; + Dirty(mind.Value.Owner, comp); + + _adminLog.Add(LogType.ObjectiveSummary, $"{ToPrettyString(mind.Value.Comp.OwnedEntity)} wrote objective summery: {msg.Summary}"); + } + + private void OnEvacShuttleLeft(EvacShuttleLeftEvent args) + { + var allMinds = _mind.GetAliveHumans(); + + // Assumes the assistant is still there at the end of the round. + foreach (var mind in allMinds) + { + // Only send the popup to people with objectives. + if (mind.Comp.Objectives.Count == 0) + continue; + + if (!_mind.TryGetSession(mind, out var session)) + continue; + + RaiseNetworkEvent(new CustomObjectiveSummaryOpenMessage(), session); + } + } + + private void OnRoundEnd(RoundEndMessageEvent ev) + { + var allMinds = _mind.GetAliveHumans(); + + foreach (var mind in allMinds) + { + if (mind.Comp.Objectives.Count == 0) + continue; + + _feedback.SendPopupMind(mind, "RemoveGreentextPopup"); + } + } +} diff --git a/Content.Shared.Database/LogType.cs b/Content.Shared.Database/LogType.cs index b5bbbc4577..1b8d32ad25 100644 --- a/Content.Shared.Database/LogType.cs +++ b/Content.Shared.Database/LogType.cs @@ -393,6 +393,7 @@ public enum LogType Tile = 86, BagOfHolding = 420, //Nyano - Summary: adds bag of holding. Psionics = 421, //Nyano - Summary: ads psionic as a log type. + ObjectiveSummary = 422, // DeltaV /// /// A client has sent too many chat messages recently and is temporarily blocked from sending more. diff --git a/Content.Shared/_DV/CustomObjectiveSummary/CustomObjectiveSummaryComponent.cs b/Content.Shared/_DV/CustomObjectiveSummary/CustomObjectiveSummaryComponent.cs new file mode 100644 index 0000000000..7413b51576 --- /dev/null +++ b/Content.Shared/_DV/CustomObjectiveSummary/CustomObjectiveSummaryComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.CustomObjectiveSummary; + +/// +/// Put on a players mind if the wrote a custom summary for their objectives. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class CustomObjectiveSummaryComponent : Component +{ + /// + /// What the player wrote as their summary! + /// + [DataField, AutoNetworkedField] + public string ObjectiveSummary = ""; +} diff --git a/Content.Shared/_DV/CustomObjectiveSummary/CustomObjectiveSummeryEvents.cs b/Content.Shared/_DV/CustomObjectiveSummary/CustomObjectiveSummeryEvents.cs new file mode 100644 index 0000000000..dad19a04db --- /dev/null +++ b/Content.Shared/_DV/CustomObjectiveSummary/CustomObjectiveSummeryEvents.cs @@ -0,0 +1,42 @@ +using Lidgren.Network; +using Robust.Shared.Network; +using Robust.Shared.Serialization; + +namespace Content.Shared._DV.CustomObjectiveSummary; + +/// +/// Message from the client with what they are updating their summary to. +/// +public sealed class CustomObjectiveClientSetObjective : NetMessage +{ + public override MsgGroups MsgGroup => MsgGroups.EntityEvent; + + /// + /// The summary that the user wrote. + /// + public string Summary = string.Empty; + + public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) + { + Summary = buffer.ReadString(); + } + + public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) + { + buffer.Write(Summary); + } + + public override NetDeliveryMethod DeliveryMethod => NetDeliveryMethod.ReliableUnordered; +} + +/// +/// Clients listen for this event and when they get it, they open a popup so the player can fill out the objective summary. +/// +[Serializable, NetSerializable] +public sealed class CustomObjectiveSummaryOpenMessage : EntityEventArgs; + +/// +/// DeltaV event for when the evac shuttle leaves. +/// +[Serializable, NetSerializable] +public sealed class EvacShuttleLeftEvent : EventArgs; diff --git a/Resources/Locale/en-US/_DV/customobjectivesummary/customobjectivesummary.ftl b/Resources/Locale/en-US/_DV/customobjectivesummary/customobjectivesummary.ftl new file mode 100644 index 0000000000..9102145ae7 --- /dev/null +++ b/Resources/Locale/en-US/_DV/customobjectivesummary/customobjectivesummary.ftl @@ -0,0 +1,13 @@ +custom-objective-button-text = Write objective summary + +# UI +custom-objective-window-title = Custom objective summary +custom-objective-window-submit-button-text = Submit +custom-objective-window-submit-button-text-confirm = Confirm submission +custom-objective-window-explain = Explain how you completed your objectives here! +custom-objective-window-explain-edit = You can always edit this anytime before the round ends. + +objectives-objective = {$objective} + +# End of round +custom-objective-format = [color=#FFAEC9] {$line}[/color] diff --git a/Resources/Locale/en-US/_DV/feedbackpopup/popups/removegreentextpopup.ftl b/Resources/Locale/en-US/_DV/feedbackpopup/popups/removegreentextpopup.ftl new file mode 100644 index 0000000000..aa36e3cbb6 --- /dev/null +++ b/Resources/Locale/en-US/_DV/feedbackpopup/popups/removegreentextpopup.ftl @@ -0,0 +1,3 @@ +feedbackpopup-remove-greentext-name = Removal of greentext +feedbackpopup-remove-greentext-title = [bold]Removal of greentext / objective summary[/bold] +feedbackpopup-remove-greentext-description-0 = We have remove greentext and replaced it with a custom summery. If you would like to share any feedback about this change, please comment it below. We would like to hear what you have to say! diff --git a/Resources/Prototypes/_DV/FeedbackPopup/feedbackpopupsRemoveGreentext.yml b/Resources/Prototypes/_DV/FeedbackPopup/feedbackpopupsRemoveGreentext.yml new file mode 100644 index 0000000000..3eac2cc4ac --- /dev/null +++ b/Resources/Prototypes/_DV/FeedbackPopup/feedbackpopupsRemoveGreentext.yml @@ -0,0 +1,6 @@ +- type: feedbackPopup + id: RemoveGreentextPopup + popupName: feedbackpopup-remove-greentext-name + title: feedbackpopup-remove-greentext-title + description: + - feedbackpopup-remove-greentext-description-0 diff --git a/Resources/Prototypes/_DV/secret_weights.yml b/Resources/Prototypes/_DV/secret_weights.yml index 41017a439a..9f5ea51661 100644 --- a/Resources/Prototypes/_DV/secret_weights.yml +++ b/Resources/Prototypes/_DV/secret_weights.yml @@ -1,8 +1,8 @@ - type: weightedRandom id: SecretDeltaV weights: - Survival: 0.35 - Nukeops: 0.15 - Zombie: 0.07 - Traitor: 0.43 + Survival: 0.33 + Nukeops: 0.12 + Zombie: 0.05 + Traitor: 0.50 #Pirates: 0.15 #ahoy me bucko