Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

using System.Linq;
using Content.Client._DV.CustomObjectiveSummary;
using Content.Client.CharacterInfo;
using Content.Client.Gameplay;
using Content.Client.Stylesheets;
Expand Down Expand Up @@ -56,6 +57,7 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
[Dependency] private readonly IEntityManager _ent = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly CustomObjectiveSummaryUIController _objective = default!; // DeltaV

[UISystemDependency] private readonly CharacterInfoSystem _characterInfo = default!;
[UISystemDependency] private readonly SpriteSystem _sprite = default!;
Expand Down Expand Up @@ -205,6 +207,19 @@ private void CharacterUpdated(CharacterData data)

_window.Objectives.AddChild(objectiveControl);
}
// Begin DeltaV Additions - Custom objective summary
if (objectives.Count > 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)
{
Expand Down Expand Up @@ -281,4 +296,4 @@ private void ToggleWindow()
_window.Open();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2025 beck-thompson <107373427+beck-thompson@users.noreply.github.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

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<CustomObjectiveSummaryOpenMessage>(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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'custom-objective-window-title'}"
MinSize="300 250"
SetSize="550 370">
<BoxContainer Orientation="Vertical" Margin="10 10 20 0">
<Label HorizontalAlignment="Center" Text="{Loc 'custom-objective-window-explain'}" />
<TextEdit Name="ObjectiveSummaryTextEdit" MaxHeight="500" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MinHeight="200" />
<Label Name="CharacterLimitLabel" HorizontalAlignment="Center" StyleClasses="LabelSmall"/>
<Label HorizontalAlignment="Center" Text="{Loc 'custom-objective-window-explain-edit'}" />
<controls:ConfirmButton Name="SubmitButton" ConfirmationText="{Loc 'custom-objective-window-submit-button-text-confirm'}" Text="{Loc 'custom-objective-window-submit-button-text'}" Margin="0 10 0 10" />
</BoxContainer>
</controls:FancyWindow>
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2025 beck-thompson <107373427+beck-thompson@users.noreply.github.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

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 = 1024;

public event Action<string>? OnSubmitted;

public CustomObjectiveSummaryWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);

SubmitButton.OnPressed +=
_ => OnSubmitted?.Invoke(Rope.Collapse(ObjectiveSummaryTextEdit.TextRope));
ObjectiveSummaryTextEdit.OnTextChanged += _ => UpdateWordCount();

_mind ??= _entity.System<SharedMindSystem>();

_mind.TryGetMind(_players.LocalSession, out var mindUid, out _);

UpdateWordCount();

if (_entity.TryGetComponent<Shared._DV.CustomObjectiveSummary.CustomObjectiveSummaryComponent>(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;
}
}
33 changes: 33 additions & 0 deletions Content.Server/Objectives/ObjectivesSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
using Content.Goobstation.Common.ServerCurrency;
using Content.Goobstation.Shared.ManifestListings;
using Content.Server.Objectives.Commands;
using Content.Shared._DV.CustomObjectiveSummary;
using Content.Shared.CCVar;
using Content.Shared.Prototypes;
using Content.Shared.Roles.Jobs;
Expand Down Expand Up @@ -242,6 +243,8 @@ private void AddSummary(StringBuilder result, string agent, List<(EntityUid, str
$"{username:subject} achieved {progress}% of objective {objectiveTitle}");

agentSummary.Append("- ");
// Omu: if you're going back to good ol' pinktext after green+pinktext drops,
// start a multiline comment starting here.
if (!_showGreentext)
{
agentSummary.AppendLine(objectiveTitle);
Expand Down Expand Up @@ -292,10 +295,40 @@ private void AddSummary(StringBuilder result, string agent, List<(EntityUid, str
("progress", progress)
));
}
// Omu: future multiline comment ends here
// Begin DeltaV Additions - Generic objective
/* Omu: Green+PinkText
agentSummary.AppendLine(Loc.GetString(
"objectives-objective",
("objective", objectiveTitle)
));
*/
}
}

var successRate = totalObjectives > 0 ? (float)completedObjectives / totalObjectives : 0f;
// Begin DeltaV Additions - custom objective response.
if (TryComp<CustomObjectiveSummaryComponent>(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));
}

Expand Down
2 changes: 2 additions & 0 deletions Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
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;
Expand Down Expand Up @@ -288,6 +289,7 @@ private void OnEmergencyFTL(EntityUid uid, EmergencyShuttleComponent component,
};
_deviceNetworkSystem.QueuePacket(uid, null, payload, netComp.TransmitFrequency);
}
RaiseLocalEvent(new EvacShuttleLeftEvent()); // DeltaV
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: 2025 beck-thompson <107373427+beck-thompson@users.noreply.github.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

using Content.Server.Administration.Logs;
using Content.Shared._DV.CustomObjectiveSummary;
using Content.Shared.Database;
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!;

public override void Initialize()
{
SubscribeLocalEvent<EvacShuttleLeftEvent>(OnEvacShuttleLeft);

_net.RegisterNetMessage<CustomObjectiveClientSetObjective>(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<CustomObjectiveSummaryComponent>(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;

RaiseNetworkEvent(new CustomObjectiveSummaryOpenMessage(), mind.Owner);
}
}
}
1 change: 1 addition & 0 deletions Content.Shared.Database/LogType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ public enum LogType
/// Tiles related interactions.
/// </summary>
Tile = 86,
ObjectiveSummary = 422, // DeltaV

/// <summary>
/// A client has sent too many chat messages recently and is temporarily blocked from sending more.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: 2025 beck-thompson <107373427+beck-thompson@users.noreply.github.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

using Robust.Shared.GameStates;

namespace Content.Shared._DV.CustomObjectiveSummary;

/// <summary>
/// Put on a players mind if the wrote a custom summary for their objectives.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class CustomObjectiveSummaryComponent : Component
{
/// <summary>
/// What the player wrote as their summary!
/// </summary>
[DataField, AutoNetworkedField]
public string ObjectiveSummary = "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2025 beck-thompson <107373427+beck-thompson@users.noreply.github.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

using Lidgren.Network;
using Robust.Shared.Network;
using Robust.Shared.Serialization;

namespace Content.Shared._DV.CustomObjectiveSummary;

/// <summary>
/// Message from the client with what they are updating their summary to.
/// </summary>
public sealed class CustomObjectiveClientSetObjective : NetMessage
{
public override MsgGroups MsgGroup => MsgGroups.EntityEvent;

/// <summary>
/// The summary that the user wrote.
/// </summary>
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;
}

/// <summary>
/// Clients listen for this event and when they get it, they open a popup so the player can fill out the objective summary.
/// </summary>
[Serializable, NetSerializable]
public sealed class CustomObjectiveSummaryOpenMessage : EntityEventArgs;

/// <summary>
/// DeltaV event for when the evac shuttle leaves.
/// </summary>
[Serializable, NetSerializable]
public sealed class EvacShuttleLeftEvent : EventArgs;
Original file line number Diff line number Diff line change
@@ -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]
Loading