mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-29 15:47:26 +00:00
Compare commits
154
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6edb3a2961 | ||
|
|
ce0410c41e | ||
|
|
d800d4a8c1 | ||
|
|
cea62ee53d | ||
|
|
c3bb3f001a | ||
|
|
a332245ff0 | ||
|
|
f94cab57df | ||
|
|
742524b2ff | ||
|
|
cdb104ee2e | ||
|
|
180281ae0d | ||
|
|
0a3507c93b | ||
|
|
2efc9871db | ||
|
|
814ef07035 | ||
|
|
657f1fd534 | ||
|
|
a8e51dffc3 | ||
|
|
84c7e3d877 | ||
|
|
d151384db8 | ||
|
|
7bcc06b46a | ||
|
|
e409e98bf6 | ||
|
|
760af2e261 | ||
|
|
ebc0340fc9 | ||
|
|
e8b6c42de4 | ||
|
|
5e0d668f60 | ||
|
|
2c1084da78 | ||
|
|
9304aad604 | ||
|
|
5132e64a0f | ||
|
|
d2bf1e9a39 | ||
|
|
865d254cd4 | ||
|
|
29ec2e659f | ||
|
|
690f64d592 | ||
|
|
b6e018eb18 | ||
|
|
8bcddf4333 | ||
|
|
34940ecde7 | ||
|
|
dafd97e6e1 | ||
|
|
593d35903d | ||
|
|
1856f65a18 | ||
|
|
9066c4af7f | ||
|
|
cf9a10af4e | ||
|
|
2d7af5622f | ||
|
|
974f5ff5fa | ||
|
|
88dc8af9a4 | ||
|
|
7c33759cbe | ||
|
|
baf009af3a | ||
|
|
b59dfa8991 | ||
|
|
e43a587ede | ||
|
|
00d77213c6 | ||
|
|
389a616138 | ||
|
|
07b029ff2b | ||
|
|
1043b40246 | ||
|
|
d0385ab8bb | ||
|
|
03347ed416 | ||
|
|
e9f0148287 | ||
|
|
0f7131d9e7 | ||
|
|
bf9b42c76c | ||
|
|
a635429d3e | ||
|
|
bd1e4224cd | ||
|
|
568621858e | ||
|
|
3df2209227 | ||
|
|
1da052e36f | ||
|
|
66b95a46ed | ||
|
|
92ba9edeee | ||
|
|
680092719f | ||
|
|
1afbadd404 | ||
|
|
2c190cf2f2 | ||
|
|
17d798a3e8 | ||
|
|
287714d87b | ||
|
|
029aaf8127 | ||
|
|
bbf1bec942 | ||
|
|
bd34a94587 | ||
|
|
86c54228f8 | ||
|
|
c66bf7c249 | ||
|
|
fd0f01e49a | ||
|
|
cfddb40c2f | ||
|
|
19d4eb8489 | ||
|
|
9bb7925bd2 | ||
|
|
e340ddcec9 | ||
|
|
e0b6261454 | ||
|
|
d741daca19 | ||
|
|
970c8d71b5 | ||
|
|
b6801b2fe5 | ||
|
|
5cd26f877e | ||
|
|
105ad32f98 | ||
|
|
e3566a4cc9 | ||
|
|
5e63549b8c | ||
|
|
5b08961c35 | ||
|
|
3c6ab7b676 | ||
|
|
23e55c9c40 | ||
|
|
ffa442f844 | ||
|
|
6d75455dd1 | ||
|
|
0d89320364 | ||
|
|
1984cc2118 | ||
|
|
3248ee4715 | ||
|
|
b2b08e698e | ||
|
|
3a612b9450 | ||
|
|
9746e92479 | ||
|
|
9d723af15d | ||
|
|
8e925a02b9 | ||
|
|
d898300c5c | ||
|
|
4294df8fd7 | ||
|
|
365209174b | ||
|
|
0c852409b3 | ||
|
|
9fb8bab84e | ||
|
|
c7e1c980b6 | ||
|
|
0516e97461 | ||
|
|
fd5b16abb3 | ||
|
|
1a9281ab63 | ||
|
|
c9038be25f | ||
|
|
b3cf460bda | ||
|
|
7fddf52368 | ||
|
|
4a96535892 | ||
|
|
93f614fdfc | ||
|
|
40997fb4a4 | ||
|
|
c3adbb32f1 | ||
|
|
3974a0331b | ||
|
|
92c6c8cfa3 | ||
|
|
ea313aae4a | ||
|
|
272c38a6d0 | ||
|
|
7f11e1eebb | ||
|
|
06429d050a | ||
|
|
ed07a12d24 | ||
|
|
493db6d129 | ||
|
|
d07f16f43f | ||
|
|
f580f3b060 | ||
|
|
e7869d8221 | ||
|
|
398e3eb75f | ||
|
|
0637a82e40 | ||
|
|
206ded17c8 | ||
|
|
847f534ff6 | ||
|
|
b38f0b1a59 | ||
|
|
efbeb5bf68 | ||
|
|
1f09c717f3 | ||
|
|
52ed881e2c | ||
|
|
393e1951a2 | ||
|
|
ca7c7daf6c | ||
|
|
697da0679a | ||
|
|
1b75cad94f | ||
|
|
82e5d9b924 | ||
|
|
e44717f262 | ||
|
|
9c83b91850 | ||
|
|
f027ae4566 | ||
|
|
724cd1c9b8 | ||
|
|
f44178bec3 | ||
|
|
877267cd31 | ||
|
|
3572bf4739 | ||
|
|
7867fb7f1f | ||
|
|
a3b1059c21 | ||
|
|
076d960072 | ||
|
|
acb6677aff | ||
|
|
c916f9a90b | ||
|
|
71b6dfc0b1 | ||
|
|
5fe8a394ba | ||
|
|
69d3d96d79 | ||
|
|
1e6f69361d | ||
|
|
cb71e8159c |
+10
-19
@@ -1,34 +1,25 @@
|
||||
# Last match in file takes precedence.
|
||||
# See https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#example-of-a-codeowners-file
|
||||
|
||||
# C# code
|
||||
/Content.*/ @DeltaV-Station/maintainers
|
||||
|
||||
# Any assets
|
||||
/Resources/ @DeltaV-Station/maintainers
|
||||
* @DeltaV-Station/maintainers
|
||||
|
||||
# Server config files
|
||||
/Resources/ConfigPresets/ @Toby222
|
||||
/Resources/ConfigPresets/ @DeltaV-Station/lead-maintainers
|
||||
|
||||
# Github workflows
|
||||
/.github/ @DeltaV-Station/lead-maintainers
|
||||
|
||||
# Lobby art and music - automatically direction issues since its immediately visible to players
|
||||
/Resources/Audio/Lobby/ @DeltaV-Station/direction
|
||||
/Resources/Textures/LobbyScreens/ @DeltaV-Station/direction
|
||||
/Resources/Audio/Lobby/ @DeltaV-Station/direction @DeltaV-Station/maintainers
|
||||
/Resources/Textures/LobbyScreens/ @DeltaV-Station/direction @DeltaV-Station/maintainers
|
||||
|
||||
# Maps
|
||||
/Resources/Maps/ @DeltaV-Station/maptainers
|
||||
/Resources/Prototypes/Maps/ @DeltaV-Station/maptainers
|
||||
/Content.IntegrationTests/Tests/PostMapInitTest.cs @DeltaV-Station/maptainers
|
||||
/Content.IntegrationTests/Tests/PostMapInitTest.cs @DeltaV-Station/maptainers @DeltaV-Station/maintainers
|
||||
|
||||
# Server rules
|
||||
/Resources/ServerInfo/Guidebook/_DV/Rules/ @DeltaV-Station/head-administrators
|
||||
|
||||
# Wiki
|
||||
/.wiki/ @DeltaV-Station/direction
|
||||
|
||||
# Tools and scripts
|
||||
/Tools/ @Toby222
|
||||
|
||||
# Workflows, codeowners, templates, etc.
|
||||
/.github/ @Toby222
|
||||
|
||||
# Standalone files in the root repo
|
||||
/* @Toby222
|
||||
/.wiki/ @DeltaV-Station/direction
|
||||
@@ -26,7 +26,7 @@ This just saves them the trouble. Note that AGPL or MIT licensing is only applic
|
||||
- [ ] I confirm that I am the creator of the code in this PR, and allow licensing it under the following license(s), or that the original author(s) has given me permission to do so:
|
||||
- [X] AGPL (https://github.com/DeltaV-Station/Delta-v/blob/master/LICENSE-AGPLv3.txt) <!-- This one is required to contribute to DeltaV -->
|
||||
- [X] MIT (https://github.com/DeltaV-Station/Delta-v/blob/master/LICENSE-MIT.txt) <!-- Optional - A lot of other SS14 forks use MIT -->
|
||||
<!-- Feel free to add more licenses as you see fit -->
|
||||
<!-- Feel free to add more licenses as you see fit -->
|
||||
|
||||
**Changelog**
|
||||
<!-- Add a Changelog entry to make players aware of new features or changes that could affect gameplay.
|
||||
|
||||
@@ -59,7 +59,7 @@ public sealed partial class BountyEntry : BoxContainer
|
||||
|
||||
var claimedByText = string.IsNullOrEmpty(bounty.ClaimedBy) ? Loc.GetString("bounty-console-claimed-by-none") : bounty.ClaimedBy;
|
||||
ClaimedBylabel.SetMarkup(Loc.GetString("bounty-console-claimed-by", ("claimant", claimedByText)));
|
||||
StatusLabel.SetMarkup(Loc.GetString($"bounty-console-formatted-status-{bounty.Status.ToString()}"));
|
||||
StatusLabel.SetMarkup(Loc.GetString($"bounty-console-status-formatted-{bounty.Status.ToString()}"));
|
||||
// End DeltaV bounty claiming
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace Content.Client.Communications.UI
|
||||
_menu.OnBroadcast += BroadcastButtonPressed;
|
||||
_menu.OnAlertLevel += AlertLevelSelected;
|
||||
_menu.OnEmergencyLevel += EmergencyShuttleButtonPressed;
|
||||
_menu.OnExfiltrationLevel += ExfiltrationShuttleButtonPressed; // DeltaV - Exfiltration shuttle
|
||||
}
|
||||
|
||||
public void AlertLevelSelected(string level)
|
||||
@@ -48,13 +47,6 @@ namespace Content.Client.Communications.UI
|
||||
CallShuttle();
|
||||
}
|
||||
|
||||
// Begin DeltaV - Exfiltration Shuttle
|
||||
public void ExfiltrationShuttleButtonPressed()
|
||||
{
|
||||
SendMessage(new CommunicationsConsoleExfiltrationShuttleMessage(!_menu!.CountdownStarted));
|
||||
}
|
||||
// End DeltaV - Exfiltration Shuttle
|
||||
|
||||
public void AnnounceButtonPressed(string message)
|
||||
{
|
||||
var maxLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength);
|
||||
@@ -94,13 +86,6 @@ namespace Content.Client.Communications.UI
|
||||
_menu.CurrentLevel = commsState.CurrentAlert;
|
||||
_menu.CountdownEnd = commsState.ExpectedCountdownEnd;
|
||||
|
||||
|
||||
// Begin DeltaV - Exfiltration Shuttle
|
||||
_menu.CanExfiltrate = commsState.CanCall;
|
||||
_menu.ExfiltrationCountdownEnd = commsState.ExpectedExfiltrationCountdownEnd;
|
||||
_menu.ExfiltrationShuttleButton.Disabled = !_menu.CanExfiltrate;
|
||||
// End DeltaV - Exfiltration Shuttle
|
||||
|
||||
_menu.UpdateCountdown();
|
||||
_menu.UpdateAlertLevels(commsState.AlertLevels, _menu.CurrentLevel);
|
||||
_menu.AlertLevelButton.Disabled = !_menu.AlertLevelSelectable;
|
||||
|
||||
@@ -52,19 +52,10 @@
|
||||
|
||||
<RichTextLabel Name="CountdownLabel"/>
|
||||
|
||||
<!-- DeltaV - Exfiltration Shuttle -->
|
||||
<RichTextLabel Name="ExfiltrationCountdownLabel"/>
|
||||
|
||||
<Button Name="EmergencyShuttleButton"
|
||||
Access="Public"
|
||||
Text="Placeholder Text"
|
||||
ToolTip="{Loc 'comms-console-menu-emergency-shuttle-button-tooltip'}"/>
|
||||
|
||||
<!-- DeltaV - Exfiltration Shuttle -->
|
||||
<Button Name="ExfiltrationShuttleButton"
|
||||
Access="Public"
|
||||
Text="Placeholder Text"
|
||||
ToolTip="{Loc 'comms-console-menu-exfiltration-shuttle-button-tooltip'}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
@@ -29,12 +29,6 @@ namespace Content.Client.Communications.UI
|
||||
public event Action<string>? OnAnnounce;
|
||||
public event Action<string>? OnBroadcast;
|
||||
|
||||
// Begin DeltaV - Exfiltration Shuttle
|
||||
public bool CanExfiltrate;
|
||||
public TimeSpan? ExfiltrationCountdownEnd;
|
||||
public event Action? OnExfiltrationLevel;
|
||||
// End DeltaV - Exfiltration Shuttle
|
||||
|
||||
public CommunicationsConsoleMenu()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -78,18 +72,12 @@ namespace Content.Client.Communications.UI
|
||||
|
||||
EmergencyShuttleButton.OnPressed += _ => OnEmergencyLevel?.Invoke();
|
||||
EmergencyShuttleButton.Disabled = !CanCall;
|
||||
|
||||
// Begin DeltaV - Exfiltration Shuttle
|
||||
ExfiltrationShuttleButton.OnPressed += _ => OnExfiltrationLevel?.Invoke();
|
||||
ExfiltrationShuttleButton.Disabled = !CanExfiltrate;
|
||||
// End DeltaV - Exfiltration Shuttle
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
UpdateCountdown();
|
||||
UpdateExfiltrationCountdown(); // DeltaV - Exfiltration shuttle
|
||||
}
|
||||
|
||||
// The current alert could make levels unselectable, so we need to ensure that the UI reacts properly.
|
||||
@@ -129,25 +117,6 @@ namespace Content.Client.Communications.UI
|
||||
}
|
||||
}
|
||||
|
||||
// Begin DeltaV - Exfiltration Shuttle
|
||||
public void UpdateExfiltrationCountdown()
|
||||
{
|
||||
if (ExfiltrationCountdownEnd is null)
|
||||
{
|
||||
ExfiltrationCountdownLabel.SetMessage(string.Empty);
|
||||
ExfiltrationShuttleButton.Text = Loc.GetString("comms-console-menu-call-exfiltration");
|
||||
return;
|
||||
}
|
||||
|
||||
var exfiltrationDiff = MathHelper.Max((ExfiltrationCountdownEnd - _timing.CurTime) ?? TimeSpan.Zero, TimeSpan.Zero);
|
||||
ExfiltrationShuttleButton.Text = Loc.GetString("comms-console-menu-recall-exfiltration");
|
||||
|
||||
var exfiltrationInfoText = Loc.GetString("comms-console-menu-exfiltration-time-remaining",
|
||||
("time", exfiltrationDiff.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture)));
|
||||
ExfiltrationCountdownLabel.SetMessage(exfiltrationInfoText);
|
||||
}
|
||||
// End DeltaV - Exfiltration Shuttle
|
||||
|
||||
public void UpdateCountdown()
|
||||
{
|
||||
if (!CountdownStarted)
|
||||
|
||||
@@ -179,9 +179,12 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
||||
var unborgable = _unborgable.IsUnborgable(target.Value);
|
||||
var redshirt = _redshirt.IsRedshirt(target.Value) && mobStateComponent?.CurrentState == MobState.Dead; // DeltaV - Redshirt
|
||||
var uncloneable = _uncloneable.IsUncloneable(target.Value) && mobStateComponent?.CurrentState == MobState.Dead; // DeltaV - Unclonable
|
||||
|
||||
// I'm just going to lump them all into 'extraTraitAlerts' to group our modifications as a single bool in the next check
|
||||
var extraTraitAlerts = unborgable || redshirt || uncloneable;
|
||||
// END DeltaV
|
||||
|
||||
var showAlerts = state.Unrevivable == true || state.Bleeding == true;
|
||||
var showAlerts = state.Unrevivable == true || state.Bleeding == true || extraTraitAlerts; // DeltaV - extra alerts
|
||||
|
||||
AlertsDivider.Visible = showAlerts;
|
||||
AlertsContainer.Visible = showAlerts;
|
||||
|
||||
@@ -32,10 +32,10 @@ namespace Content.Client.Labels.UI
|
||||
}
|
||||
|
||||
_window.OnLabelChanged += OnLabelChanged;
|
||||
_window.OnLabelSelected += OnLabelSelected; // Starlight
|
||||
Reload();
|
||||
_window.SetInitialLabelState(); // Must be after Reload() has set the label text
|
||||
}
|
||||
|
||||
private void OnLabelChanged(string newLabel)
|
||||
{
|
||||
// Focus moment
|
||||
@@ -45,7 +45,17 @@ namespace Content.Client.Labels.UI
|
||||
|
||||
SendPredictedMessage(new HandLabelerLabelChangedMessage(newLabel));
|
||||
}
|
||||
|
||||
// Starlight start
|
||||
private void OnLabelSelected(string label)
|
||||
{
|
||||
// Update UI and send message to sync with server
|
||||
if (_window != null)
|
||||
{
|
||||
_window.SetCurrentLabel(label);
|
||||
}
|
||||
SendPredictedMessage(new HandLabelerLabelChangedMessage(label));
|
||||
}
|
||||
// Starlight End
|
||||
public void Reload()
|
||||
{
|
||||
if (_window == null || !_entManager.TryGetComponent(Owner, out HandLabelerComponent? component))
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Title="{Loc 'hand-labeler-ui-header'}"
|
||||
SetWidth="400"
|
||||
MinWidth="150">
|
||||
MinWidth="150"
|
||||
SetHeight="400"> <!-- Starlight - include gfx and set default height to include saved labels section nicely -->
|
||||
<BoxContainer Orientation="Vertical" SeparationOverride="4">
|
||||
<Label Name="CurrentTextLabel" Text="{Loc 'hand-labeler-current-text-label'}" />
|
||||
<LineEdit Name="LabelLineEdit" />
|
||||
@@ -10,5 +12,19 @@
|
||||
<Button Name="ResetLabelButton" Text="{Loc 'hand-labeler-ui-reset-label-text'}" StyleClasses="OpenRight" />
|
||||
<Button Name="ClearLabelButton" Text="{Loc 'hand-labeler-ui-clear-label-text'}" StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
<!-- Starlight Begin -->
|
||||
<Label Text="{Loc 'hand-labeler-saved-labels'}" />
|
||||
<PanelContainer VerticalExpand="True" SizeFlagsStretchRatio="1">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<ItemList Name="SavedLabelsList" VerticalExpand="True" SelectMode="Button" />
|
||||
</PanelContainer>
|
||||
|
||||
<BoxContainer Orientation="Horizontal" Align="Center">
|
||||
<Button Name="SaveLabelButton" Text="{Loc 'hand-labeler-save-label-text'}" StyleClasses="OpenRight" />
|
||||
<Button Name="DeleteLabelButton" Text="{Loc 'hand-labeler-delete-label-text'}" StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
<!-- Starlight end -->
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Client.UserInterface.Controls; // Starlight
|
||||
using Robust.Shared.Configuration; // Starlight
|
||||
using Content.Shared.Starlight.CCVar; // Starlight
|
||||
|
||||
namespace Content.Client.Labels.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HandLabelerWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!; // Starlight
|
||||
|
||||
public event Action<string>? OnLabelChanged;
|
||||
public event Action<string>? OnLabelSelected; // Starlight
|
||||
|
||||
/// <summary>
|
||||
/// Is the user currently entering text into the control?
|
||||
@@ -17,10 +23,14 @@ namespace Content.Client.Labels.UI
|
||||
|
||||
private string _label = string.Empty;
|
||||
private string _initialLabel = string.Empty;
|
||||
private bool _deleteMode = false; // Starlight
|
||||
private List<string> _savedLabels = []; // Starlight
|
||||
|
||||
public HandLabelerWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this); // Starlight
|
||||
LoadSavedLabelsFromCVar(); // Starlight
|
||||
|
||||
LabelLineEdit.OnTextChanged += e =>
|
||||
{
|
||||
@@ -37,6 +47,42 @@ namespace Content.Client.Labels.UI
|
||||
};
|
||||
ResetLabelButton.OnPressed += _ => LabelLineEdit.SetText(_initialLabel, true);
|
||||
ClearLabelButton.OnPressed += _ => LabelLineEdit.SetText("", true);
|
||||
|
||||
// Starlight Begin
|
||||
SaveLabelButton.OnPressed += _ =>
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(_label) && !_savedLabels.Contains(_label))
|
||||
{
|
||||
_savedLabels.Add(_label);
|
||||
SaveSavedLabelsToCVar();
|
||||
UpdateSavedLabels(_savedLabels);
|
||||
}
|
||||
};
|
||||
|
||||
DeleteLabelButton.OnPressed += _ =>
|
||||
{
|
||||
_deleteMode = !_deleteMode;
|
||||
DeleteLabelButton.Pressed = _deleteMode;
|
||||
UpdateButtons();
|
||||
};
|
||||
|
||||
SavedLabelsList.OnItemSelected += args =>
|
||||
{
|
||||
if (_deleteMode)
|
||||
{
|
||||
_savedLabels.RemoveAt(args.ItemIndex);
|
||||
SaveSavedLabelsToCVar();
|
||||
UpdateSavedLabels(_savedLabels);
|
||||
UpdateButtons();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SavedLabelsList[args.ItemIndex].Metadata is string label)
|
||||
{
|
||||
OnLabelSelected?.Invoke(label);
|
||||
}
|
||||
}
|
||||
}; // Starlight End
|
||||
}
|
||||
|
||||
protected override void Opened()
|
||||
@@ -70,15 +116,68 @@ namespace Content.Client.Labels.UI
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
// Starlight begin
|
||||
public void UpdateSavedLabels(List<string> labels)
|
||||
{
|
||||
var items = new List<ItemList.Item>();
|
||||
foreach (var label in labels)
|
||||
{
|
||||
var item = new ItemList.Item(SavedLabelsList)
|
||||
{
|
||||
Text = label,
|
||||
Metadata = label
|
||||
};
|
||||
items.Add(item);
|
||||
}
|
||||
SavedLabelsList.SetItems(items);
|
||||
UpdateButtons();
|
||||
}
|
||||
// Starlight end
|
||||
|
||||
public void UpdateButtons()
|
||||
{
|
||||
ResetLabelButton.Disabled = (LabelLineEdit.Text == _initialLabel);
|
||||
ClearLabelButton.Disabled = (LabelLineEdit.Text == "");
|
||||
}
|
||||
|
||||
// Starlight begin
|
||||
var labelExists = false;
|
||||
foreach (var item in SavedLabelsList)
|
||||
{
|
||||
if ((item.Metadata as string) == _label.Trim())
|
||||
{
|
||||
labelExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SaveLabelButton.Disabled = string.IsNullOrWhiteSpace(_label) || labelExists;
|
||||
|
||||
DeleteLabelButton.Text = _deleteMode
|
||||
? Loc.GetString("hand-labeler-delete-mode-text")
|
||||
: Loc.GetString("hand-labeler-delete-label-text");
|
||||
|
||||
// Starlight end
|
||||
}
|
||||
public void SetMaxLabelLength(int maxLength)
|
||||
{
|
||||
LabelLineEdit.IsValid = s => s.Length <= maxLength;
|
||||
}
|
||||
// Starlight begin
|
||||
private void LoadSavedLabelsFromCVar()
|
||||
{
|
||||
var savedLabelsStr = _cfg.GetCVar(StarlightCCVars.HandLabelerSavedLabels);
|
||||
if (!string.IsNullOrEmpty(savedLabelsStr))
|
||||
{
|
||||
_savedLabels = new List<string>(savedLabelsStr.Split('\n', StringSplitOptions.RemoveEmptyEntries));
|
||||
UpdateSavedLabels(_savedLabels);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveSavedLabelsToCVar()
|
||||
{
|
||||
var savedLabelsStr = string.Join('\n', _savedLabels);
|
||||
_cfg.SetCVar(StarlightCCVars.HandLabelerSavedLabels, savedLabelsStr);
|
||||
_cfg.SaveToFile();
|
||||
}
|
||||
// Starlight end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ using Content.Shared.Preferences;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Client._CD.Records.UI; // DeltaV
|
||||
using Content.Shared._DV.Body.Systems; // DeltaV
|
||||
|
||||
namespace Content.Client.Lobby.UI;
|
||||
|
||||
@@ -193,6 +195,7 @@ public sealed partial class HumanoidProfileEditor
|
||||
RefreshLoadouts();
|
||||
UpdateSexControls(); // update sex for new species
|
||||
UpdateSpeciesGuidebookIcon();
|
||||
UpdateHeightControls(); // DeltaV
|
||||
ReloadPreview();
|
||||
}
|
||||
|
||||
@@ -204,23 +207,22 @@ public sealed partial class HumanoidProfileEditor
|
||||
ReloadProfilePreview();
|
||||
}
|
||||
|
||||
|
||||
private void UpdateHeightControls()
|
||||
{
|
||||
if (Profile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var species = _species.Find(x => x.ID == Profile.Species);
|
||||
if (species != null)
|
||||
_defaultHeight = species.DefaultHeight;
|
||||
var prototype = _prototypeManager.Index(Profile.Species);
|
||||
_defaultHeight = prototype.DefaultHeight;
|
||||
|
||||
var prototype = _prototypeManager.Index<SpeciesPrototype>(Profile.Species);
|
||||
var sliderPercent = (Profile.Height - prototype.MinHeight) /
|
||||
(prototype.MaxHeight - prototype.MinHeight);
|
||||
CDHeightSlider.Value = sliderPercent;
|
||||
CDHeight.Text = Profile.Height.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
var scaleReference = _defaultHeight * prototype.BaseScale.Y;
|
||||
var newHeight = MathF.Round(MathHelper.Lerp(prototype.MinHeight, prototype.MaxHeight, sliderPercent), 2);
|
||||
CDHeightLabel.Text = UnitConversion.GetMetricAndImperialDisplayFromScale(scaleReference * newHeight);
|
||||
CDPullSpeedReductionLabel.Text = SmallCharacterSystem.GetPullSpeedPenaltyDisplayFromScale(newHeight);
|
||||
}
|
||||
// End CD - Character Records
|
||||
|
||||
|
||||
@@ -73,9 +73,13 @@
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-height-label'}" />
|
||||
<Control HorizontalExpand="True" />
|
||||
<Label Name="CDHeightLabel" />
|
||||
<Slider Name="CDHeightSlider" HorizontalAlignment="Right" SetWidth="300" MinValue="0.0" MaxValue="1.0"/>
|
||||
<LineEdit HorizontalAlignment="Right" Name="CDHeight" MinSize="60 0" Text="1.0" />
|
||||
<Button Name="CDHeightReset" Text="{Loc 'humanoid-profile-editor-reset-height-button'}" HorizontalAlignment="Right"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-height-pull-speed-penalty-label'}" />
|
||||
<Control HorizontalExpand="True" />
|
||||
<Label Name="CDPullSpeedReductionLabel" />
|
||||
</BoxContainer>
|
||||
<!-- End CD - Character Records -->
|
||||
<!-- Sex -->
|
||||
|
||||
@@ -2,9 +2,6 @@ using Content.Client.Humanoid;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Client.Sprite;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared._DV.Species; // DeltaV - Species hider
|
||||
using Content.Shared.Body;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid;
|
||||
@@ -22,13 +19,8 @@ using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
// Begin CD - Character Records
|
||||
using System.Globalization;
|
||||
using Content.Client._CD.Records.UI;
|
||||
using Content.Shared._CD.Records;
|
||||
// End CD - Character Records
|
||||
using Content.Shared._DV.Traits;
|
||||
using Content.Shared.Humanoid.Prototypes; // DV - Traits
|
||||
using Content.Client._CD.Records.UI; // CD - Character Records
|
||||
using Content.Shared._DV.Body.Systems; // DV - Traits
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
@@ -221,34 +213,18 @@ namespace Content.Client.Lobby.UI
|
||||
// Begin CD - Character Records
|
||||
#region CDHeight
|
||||
|
||||
CDHeight.OnTextChanged += args =>
|
||||
{
|
||||
if (Profile is null || !float.TryParse(args.Text, out var newHeight))
|
||||
return;
|
||||
|
||||
var prototype = _prototypeManager.Index<SpeciesPrototype>(Profile.Species);
|
||||
newHeight = MathF.Round(Math.Clamp(newHeight, prototype.MinHeight, prototype.MaxHeight), 2);
|
||||
|
||||
// The percentage between the start and end numbers, aka "inverse lerp"
|
||||
var sliderPercent = (newHeight - prototype.MinHeight) /
|
||||
(prototype.MaxHeight - prototype.MinHeight);
|
||||
CDHeightSlider.Value = sliderPercent;
|
||||
|
||||
SetProfileHeight(newHeight);
|
||||
};
|
||||
|
||||
CDHeightReset.OnPressed += _ =>
|
||||
{
|
||||
CDHeight.SetText(_defaultHeight.ToString(CultureInfo.InvariantCulture), true);
|
||||
};
|
||||
|
||||
CDHeightSlider.OnValueChanged += _ =>
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
var prototype = _prototypeManager.Index<SpeciesPrototype>(Profile.Species);
|
||||
|
||||
var prototype = _prototypeManager.Index(Profile.Species);
|
||||
var newHeight = MathF.Round(MathHelper.Lerp(prototype.MinHeight, prototype.MaxHeight, CDHeightSlider.Value), 2);
|
||||
CDHeight.Text = newHeight.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
var speciesScale = prototype.BaseScale.Y;
|
||||
|
||||
CDHeightLabel.Text = UnitConversion.GetMetricAndImperialDisplayFromScale(newHeight * speciesScale);
|
||||
CDPullSpeedReductionLabel.Text = SmallCharacterSystem.GetPullSpeedPenaltyDisplayFromScale(newHeight);
|
||||
SetProfileHeight(newHeight);
|
||||
};
|
||||
|
||||
|
||||
@@ -70,6 +70,11 @@ public sealed class ConfirmButton : Button
|
||||
[ViewVariables]
|
||||
public bool IsConfirming = false;
|
||||
|
||||
// Begin DeltaV
|
||||
[ViewVariables]
|
||||
public bool ForceDisabled = false;
|
||||
// End DeltaV
|
||||
|
||||
public ConfirmButton()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -87,7 +92,7 @@ public sealed class ConfirmButton : Button
|
||||
}
|
||||
|
||||
if (Disabled && _gameTiming.CurTime > _nextCooldown)
|
||||
Disabled = false;
|
||||
Disabled = ForceDisabled; // DeltaV
|
||||
}
|
||||
|
||||
protected override void DrawModeChanged()
|
||||
|
||||
@@ -49,11 +49,6 @@
|
||||
<Control HorizontalExpand="True" />
|
||||
<Label Name="RecordContainerSpecies" Align="Right" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-cd-records-height'}"/>
|
||||
<Control HorizontalExpand="True" />
|
||||
<Label Name="RecordContainerHeight" Align="Right" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-cd-records-weight'}"/>
|
||||
<Control HorizontalExpand="True" />
|
||||
|
||||
@@ -325,8 +325,7 @@ public sealed partial class CharacterRecordViewer : FancyWindow
|
||||
RecordContainerJob.Text = record.JobTitle; /* At some point in the future we might want to display the icon */
|
||||
RecordContainerGender.Text = record.Gender.ToString();
|
||||
RecordContainerSpecies.Text = record.Species;
|
||||
RecordContainerHeight.Text = cr.Height + " " + UnitConversion.GetImperialDisplayLength(cr.Height);
|
||||
RecordContainerWeight.Text = cr.Weight + " " + UnitConversion.GetImperialDisplayMass(cr.Weight);
|
||||
RecordContainerWeight.Text = $"{cr.Weight} ({UnitConversion.GetImperialDisplayMass(cr.Weight)})";
|
||||
RecordContainerContactName.SetValue(cr.EmergencyContactName);
|
||||
|
||||
RecordContainerEmployment.Visible = false;
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="10">
|
||||
<!-- Height, Weight -->
|
||||
<GridContainer Columns="2">
|
||||
<BoxContainer HorizontalExpand="True" SeparationOverride="2">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-cd-records-height'}" />
|
||||
<Control HorizontalExpand="True" MinSize="5 0" />
|
||||
<LineEdit Name="HeightEdit" HorizontalAlignment="Right" MinSize="60 0" />
|
||||
<Label Name="HeightImperialLabel" MinWidth="60" />
|
||||
</BoxContainer>
|
||||
<BoxContainer HorizontalExpand="True" SeparationOverride="2">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-cd-records-weight'}" />
|
||||
<Control HorizontalExpand="True" MinSize="5 0" />
|
||||
|
||||
@@ -25,14 +25,6 @@ public sealed partial class RecordEditorGui : Control
|
||||
|
||||
#region General
|
||||
|
||||
HeightEdit.OnTextChanged += args =>
|
||||
{
|
||||
if (!int.TryParse(args.Text, out var newHeight))
|
||||
return;
|
||||
UpdateImperialHeight(newHeight);
|
||||
UpdateRecords(_records.WithHeight(newHeight));
|
||||
};
|
||||
|
||||
WeightEdit.OnTextChanged += args =>
|
||||
{
|
||||
if (!int.TryParse(args.Text, out var newWeight))
|
||||
@@ -128,8 +120,6 @@ public sealed partial class RecordEditorGui : Control
|
||||
|
||||
private void UpdateWidgets()
|
||||
{
|
||||
HeightEdit.SetText(_records.Height.ToString());
|
||||
UpdateImperialHeight(_records.Height);
|
||||
WeightEdit.SetText(_records.Weight.ToString());
|
||||
UpdateImperialWeight(_records.Weight);
|
||||
ContactNameEdit.SetText(_records.EmergencyContactName);
|
||||
@@ -143,13 +133,8 @@ public sealed partial class RecordEditorGui : Control
|
||||
PostmortemEdit.SetText(_records.PostmortemInstructions);
|
||||
}
|
||||
|
||||
private void UpdateImperialHeight(int newHeight)
|
||||
{
|
||||
HeightImperialLabel.Text = UnitConversion.GetImperialDisplayLength(newHeight);
|
||||
}
|
||||
|
||||
private void UpdateImperialWeight(int newWeight)
|
||||
{
|
||||
WeightImperialLabel.Text = UnitConversion.GetImperialDisplayMass(newWeight);
|
||||
WeightImperialLabel.Text = $"({UnitConversion.GetImperialDisplayMass(newWeight)})";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,47 @@ namespace Content.Client._CD.Records.UI;
|
||||
|
||||
public static class UnitConversion
|
||||
{
|
||||
/// <summary>
|
||||
/// DeltaV - The average height of a human in centimeters. According to the US CDC, its
|
||||
/// 171 for men and 160 for women. So average of that is ~165cm.
|
||||
///
|
||||
/// Just kidding, we're going with EE's arbitrary standard of 175cm.
|
||||
/// </summary>
|
||||
private const int AVERAGE_HEIGHT_CM = 175;
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - 1.0 scale is considered average for humans, so a scale of 1 will be 175cm.
|
||||
/// Ensure that scale also includes the base species height AND the user-defined height.
|
||||
/// </summary>
|
||||
/// <param name="scale"></param>
|
||||
/// <returns></returns>
|
||||
private static int GetMetricHeightFromScale(float scale = 1)
|
||||
{
|
||||
// cast as int because we don't care about decimal
|
||||
return (int)Math.Max(scale * AVERAGE_HEIGHT_CM, 1); // can't be shorter than 1cm I guess
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Gets nicely formatted string that contains both metric and imperial measurements.
|
||||
/// With a scale of 1, it should look like... 175cm (5' 9")
|
||||
/// </summary>
|
||||
/// <param name="scale"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetMetricAndImperialDisplayFromScale(float scale = 1)
|
||||
{
|
||||
var metricHeight = GetMetricHeightFromScale(scale);
|
||||
return $"{metricHeight}cm ({GetImperialDisplayLength(metricHeight)})";
|
||||
}
|
||||
|
||||
public static string GetImperialDisplayLength(int lengthCm)
|
||||
{
|
||||
var heightIn = (int) Math.Round(lengthCm * 0.3937007874 /* cm to in*/);
|
||||
return $"({heightIn / 12}'{heightIn % 12}'')";
|
||||
var heightIn = (int)Math.Round(lengthCm * 0.3937007874 /* cm to in*/);
|
||||
return $"{heightIn / 12}'{heightIn % 12}\"";
|
||||
}
|
||||
|
||||
public static string GetImperialDisplayMass(int massKg)
|
||||
{
|
||||
var weightLbs = (int) Math.Round(massKg * 2.2046226218 /* kg to lbs */);
|
||||
return $"({weightLbs} lbs)";
|
||||
var weightLbs = (int)Math.Round(massKg * 2.2046226218 /* kg to lbs */);
|
||||
return $"{weightLbs} lbs";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared._DV.Communications;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client._DV.Communications;
|
||||
|
||||
public sealed class DVCommunicationsConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entity = default!;
|
||||
|
||||
private DVCommunicationsConsoleMenu? _menu;
|
||||
|
||||
public DVCommunicationsConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = this.CreateWindow<DVCommunicationsConsoleMenu>();
|
||||
_menu.OnMessage += SendMessage;
|
||||
if (_entity.TryGetComponent<DVCommunicationsConsoleComponent>(Owner, out var comp))
|
||||
Update((Owner, comp));
|
||||
}
|
||||
|
||||
public void Update(Entity<DVCommunicationsConsoleComponent> ent)
|
||||
{
|
||||
_menu?.Update(ent, PlayerManager.LocalEntity!.Value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<controls:FancyWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'comms-console-menu-title'}"
|
||||
MinSize="400 300">
|
||||
|
||||
<BoxContainer Name="HomeScreen" Orientation="Vertical" Margin="8">
|
||||
<Label Text="{Loc 'comms-console-menu-station-title'}" Name="StationHeaderLabel" StyleClasses="LabelKeyText" />
|
||||
<Button Name="AnnouncementTab" Text="{Loc 'comms-console-menu-tab.Announcement'}" />
|
||||
<Button Name="ScreenTab" Text="{Loc 'comms-console-menu-tab.Screen'}" />
|
||||
<Button Name="AlertLevelTab" Text="{Loc 'comms-console-menu-tab.AlertLevel'}" />
|
||||
|
||||
<Label Text="{Loc 'comms-console-menu-transit-title'}" Name="TransitHeaderLabel" StyleClasses="LabelKeyText" Margin="0 8 0 0" />
|
||||
<Button Name="ShuttlesTab" Text="{Loc 'comms-console-menu-tab.Shuttles'}" />
|
||||
|
||||
<Label Text="{Loc 'comms-console-menu-emergency-title'}" Name="EmergencyHeaderLabel" StyleClasses="LabelKeyText" Margin="0 8 0 0" />
|
||||
<Button Name="KeycardAuthenticationDeviceTab" Text="{Loc 'comms-console-menu-tab.KeycardAuthenticationDevice'}" />
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Button Name="HomeButton" Text="{Loc 'comms-console-menu-home'}" Visible="False" />
|
||||
|
||||
<BoxContainer Name="AnnouncementScreen" Visible="False" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" SeparationOverride="8" Margin="8">
|
||||
<PanelContainer StyleClasses="PanelDark" HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer SeparationOverride="8" Margin="8" HorizontalExpand="True" Orientation="Vertical">
|
||||
<Label Text="{Loc 'comms-console-menu-announcement-text'}" StyleClasses="LabelKeyText" />
|
||||
<TextEdit Name="AnnounceInput"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
MinHeight="100" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
<PanelContainer StyleClasses="PanelDark" HorizontalExpand="True">
|
||||
<Button Name="AnnounceButton" Margin="8" Text="{Loc 'comms-console-menu-send-announcement'}" />
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Name="ScreenScreen" Visible="False" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" SeparationOverride="8" Margin="8">
|
||||
<PanelContainer StyleClasses="PanelDark" HorizontalExpand="True">
|
||||
<BoxContainer SeparationOverride="8" Margin="8" HorizontalExpand="True" Orientation="Vertical">
|
||||
<Label Text="{Loc 'comms-console-menu-screen-configuration'}" StyleClasses="LabelKeyText" />
|
||||
|
||||
<BoxContainer>
|
||||
<Label Text="{Loc 'comms-console-menu-screen-contents'}" HorizontalExpand="True" />
|
||||
<OptionButton Name="ScreenContentsButton" />
|
||||
</BoxContainer>
|
||||
<BoxContainer>
|
||||
<Label Text="{Loc 'comms-console-menu-alert-level-border'}" HorizontalExpand="True" />
|
||||
<Button Name="ScreenAlertBorder" Text="{Loc 'comms-console-menu-enable-border'}" ToggleMode="True" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
|
||||
<PanelContainer StyleClasses="PanelDark">
|
||||
<BoxContainer Margin="8" Orientation="Vertical" SeparationOverride="4">
|
||||
<Label Text="{Loc 'comms-console-menu-screen-text'}" StyleClasses="LabelKeyText" />
|
||||
|
||||
<GridContainer Columns="2" HorizontalExpand="True">
|
||||
<Label Text="{Loc 'comms-console-menu-line-1'}" />
|
||||
<LineEdit Name="ScreenLine1" HorizontalExpand="True" StyleClasses="comms-console-display" />
|
||||
<Label Text="{Loc 'comms-console-menu-line-2'}" />
|
||||
<LineEdit Name="ScreenLine2" HorizontalExpand="True" StyleClasses="comms-console-display" />
|
||||
</GridContainer>
|
||||
|
||||
<Button Name="ScreenUpdateTextButton" Text="{Loc 'comms-console-menu-update-screen-text'}" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Name="ShuttlesScreen" Visible="False" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" SeparationOverride="8" Margin="8">
|
||||
<PanelContainer StyleClasses="PanelDark" HorizontalExpand="True">
|
||||
<BoxContainer Margin="8" HorizontalExpand="True" Orientation="Vertical">
|
||||
<Label Text="{Loc 'comms-console-menu-emergency-shuttle'}" StyleClasses="LabelKeyText" />
|
||||
<RichTextLabel Name="EmergencyStatus" Text="{Loc 'comms-console-menu-shuttle-not-coming'}" />
|
||||
<Button Name="ShuttleEmergencyButton" Text="{Loc 'comms-console-menu-call-emergency.True'}" HorizontalAlignment="Right" Margin="0 8 0 0" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
<PanelContainer StyleClasses="PanelDark" HorizontalExpand="True">
|
||||
<BoxContainer Margin="8" HorizontalExpand="True" Orientation="Vertical">
|
||||
<Label Text="{Loc 'comms-console-menu-exfiltration-shuttle'}" StyleClasses="LabelKeyText" />
|
||||
<RichTextLabel Name="ExfiltrationStatus" Text="{Loc 'comms-console-menu-shuttle-not-coming'}" />
|
||||
<Button Name="ShuttleExfiltrationButton" Text="{Loc 'comms-console-menu-call-exfiltration.True'}" HorizontalAlignment="Right" Margin="0 8 0 0" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Name="AlertLevelScreen" Visible="False" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" SeparationOverride="8" Margin="8">
|
||||
<PanelContainer StyleClasses="PanelDark" HorizontalExpand="True" MaxWidth="400">
|
||||
<BoxContainer Margin="8" HorizontalExpand="True" Orientation="Vertical">
|
||||
<Label Text="{Loc 'comms-console-menu-alert-level'}" StyleClasses="LabelKeyText" />
|
||||
<RichTextLabel Name="AlertLevel" HorizontalExpand="True" />
|
||||
<Button Name="AlertLevelsDropdown" Text="{Loc 'comms-console-menu-change-alert-level'}" HorizontalAlignment="Right" Margin="0 8 0 0" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Name="KeycardAuthenticationDeviceScreen" Visible="False" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" SeparationOverride="8" Margin="8">
|
||||
<PanelContainer StyleClasses="PanelDark" HorizontalExpand="True" MaxWidth="400">
|
||||
<BoxContainer Margin="8" HorizontalExpand="True" Orientation="Vertical">
|
||||
<Label Text="{Loc 'comms-console-menu-declare-mayday.title'}" StyleClasses="LabelKeyText" />
|
||||
<RichTextLabel Text="{Loc 'comms-console-menu-declare-mayday.desc'}" />
|
||||
<controls:ConfirmButton Name="CallZeta" Text="{Loc 'comms-console-menu-declare-mayday.button'}" HorizontalAlignment="Stretch" Margin="0 8 0 0" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
|
||||
<PanelContainer StyleClasses="PanelDark" HorizontalExpand="True" MaxWidth="400">
|
||||
<BoxContainer Margin="8" HorizontalExpand="True" Orientation="Vertical">
|
||||
<Label Text="{Loc 'comms-console-menu-request-codes.title'}" StyleClasses="LabelKeyText" />
|
||||
<RichTextLabel Text="{Loc 'comms-console-menu-request-codes.desc'}" />
|
||||
<controls:ConfirmButton Name="RequestCodes" Text="{Loc 'comms-console-menu-request-codes.button'}" HorizontalAlignment="Stretch" Margin="0 8 0 0" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
@@ -0,0 +1,320 @@
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared._DV.Communications;
|
||||
using Content.Shared._DV.KeycardAuthenticationDevice;
|
||||
using Content.Shared._DV.Screens;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Station;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._DV.Communications;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class DVCommunicationsConsoleMenu : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IEntityManager _entity = default!;
|
||||
|
||||
private readonly SharedStationSystem _station;
|
||||
|
||||
private readonly BoxContainer[] _screens;
|
||||
private readonly ButtonGroup _group;
|
||||
|
||||
public event Action<BoundUserInterfaceMessage>? OnMessage;
|
||||
private Entity<DVCommunicationsConsoleComponent>? _console;
|
||||
private Entity<DVStationKeycardAuthenticationDeviceComponent>? _stationKeycardAuthenticationDevice;
|
||||
private EntityUid? _user;
|
||||
|
||||
private readonly Popup _alertLevelsPopup;
|
||||
private readonly BoxContainer _alertLevels = new()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
};
|
||||
|
||||
private string _lastConfiguredLine1 = string.Empty;
|
||||
private string _lastConfiguredLine2 = string.Empty;
|
||||
|
||||
public DVCommunicationsConsoleMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_station = _entity.System<SharedStationSystem>();
|
||||
|
||||
_group = new();
|
||||
_screens = new[]
|
||||
{
|
||||
HomeScreen, AnnouncementScreen, ScreenScreen, ShuttlesScreen, AlertLevelScreen,
|
||||
KeycardAuthenticationDeviceScreen,
|
||||
};
|
||||
|
||||
HomeButton.OnPressed += _ => SetScreen(HomeScreen);
|
||||
AnnouncementTab.OnPressed += _ => SetScreen(AnnouncementScreen);
|
||||
ScreenTab.OnPressed += _ => SetScreen(ScreenScreen);
|
||||
ShuttlesTab.OnPressed += _ => SetScreen(ShuttlesScreen);
|
||||
AlertLevelTab.OnPressed += _ => SetScreen(AlertLevelScreen);
|
||||
KeycardAuthenticationDeviceTab.OnPressed += _ => SetScreen(KeycardAuthenticationDeviceScreen);
|
||||
|
||||
foreach (var value in Enum.GetValues<DVScreenContent>())
|
||||
{
|
||||
ScreenContentsButton.AddItem(Loc.GetString($"comms-console-menu-screen-content.{value}"), (int)value);
|
||||
}
|
||||
|
||||
AnnounceButton.OnPressed += OnAnnouncePressed;
|
||||
ScreenUpdateTextButton.OnPressed += OnUpdateScreenText;
|
||||
ScreenContentsButton.OnItemSelected += OnScreenContentsSelected;
|
||||
ScreenAlertBorder.OnToggled += OnScreenAlertBorderToggled;
|
||||
|
||||
ShuttleEmergencyButton.OnPressed += OnEmergencyPressed;
|
||||
ShuttleExfiltrationButton.OnPressed += OnExfiltrationPressed;
|
||||
AlertLevelsDropdown.OnPressed += OnAlertLevelsDropdownPressed;
|
||||
|
||||
CallZeta.OnPressed += OnCallZetaPressed;
|
||||
RequestCodes.OnPressed += OnRequestCodesPressed;
|
||||
|
||||
_alertLevelsPopup = new()
|
||||
{
|
||||
Children = { _alertLevels },
|
||||
};
|
||||
_alertLevelsPopup.OnPopupHide += () => _alertLevelsPopup.Orphan();
|
||||
|
||||
AnnounceInput.Placeholder = new Rope.Leaf(Loc.GetString("comms-console-menu-announcement-placeholder"));
|
||||
}
|
||||
|
||||
private void OnAlertLevelsDropdownPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
var globalPos = AlertLevelsDropdown.GlobalPosition;
|
||||
globalPos.Y += AlertLevelsDropdown.Size.Y + 1; // Place it below us, with a safety margin.
|
||||
_alertLevels.Measure(Window?.Size ?? Vector2Helpers.Infinity);
|
||||
var (minX, minY) = _alertLevels.DesiredSize;
|
||||
var box = UIBox2.FromDimensions(globalPos, new Vector2(Math.Max(minX, AlertLevelsDropdown.Width), minY));
|
||||
_alertLevelsPopup.Orphan();
|
||||
Root?.ModalRoot.AddChild(_alertLevelsPopup);
|
||||
_alertLevelsPopup.Open(box);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_console is not { } console)
|
||||
return;
|
||||
|
||||
AnnounceButton.Disabled = _timing.CurTime <= console.Comp.CanAnnounceAt;
|
||||
AlertLevelsDropdown.Disabled = console.Comp.CanSetAlertAt is null || _timing.CurTime <= console.Comp.CanSetAlertAt;
|
||||
if (AlertLevelsDropdown.Disabled)
|
||||
_alertLevelsPopup.Close();
|
||||
|
||||
if (console.Comp.ExpectedEvacuationArrival is { } evacArrival)
|
||||
{
|
||||
var diff = MathHelper.Max(evacArrival - _timing.CurTime, TimeSpan.Zero);
|
||||
var infoText = Loc.GetString($"comms-console-menu-shuttle-eta",
|
||||
("time", diff.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture)));
|
||||
EmergencyStatus.Text = infoText;
|
||||
}
|
||||
else
|
||||
{
|
||||
EmergencyStatus.Text = Loc.GetString("comms-console-menu-shuttle-not-coming");
|
||||
}
|
||||
|
||||
if (console.Comp.ExpectedExfiltrationArrival is { } exfiltrationArrival)
|
||||
{
|
||||
var diff = MathHelper.Max(exfiltrationArrival - _timing.CurTime, TimeSpan.Zero);
|
||||
var infoText = Loc.GetString($"comms-console-menu-shuttle-eta",
|
||||
("time", diff.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture)));
|
||||
ExfiltrationStatus.Text = infoText;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExfiltrationStatus.Text = Loc.GetString("comms-console-menu-shuttle-not-coming");
|
||||
}
|
||||
|
||||
UpdateKeycardAuthenticationDevice();
|
||||
}
|
||||
|
||||
private void SetScreen(Control screen)
|
||||
{
|
||||
foreach (var other in _screens)
|
||||
{
|
||||
other.Visible = false;
|
||||
}
|
||||
|
||||
screen.Visible = true;
|
||||
HomeButton.Visible = screen != HomeScreen;
|
||||
}
|
||||
|
||||
public void Update(Entity<DVCommunicationsConsoleComponent> console, EntityUid user)
|
||||
{
|
||||
_console = console;
|
||||
_user = user;
|
||||
if (_station.GetOwningStation(console) is { } station &&
|
||||
_entity.TryGetComponent<DVStationKeycardAuthenticationDeviceComponent>(station,
|
||||
out var keycardAuthenticationDevice))
|
||||
{
|
||||
_stationKeycardAuthenticationDevice = (station, keycardAuthenticationDevice);
|
||||
}
|
||||
|
||||
// If they only have the ability to announce, there's no point in having a home button
|
||||
if (console.Comp.CanAnnounce && !(console.Comp.CanAlertLevel || console.Comp.CanCallShuttles || console.Comp.CanConfigureScreens || console.Comp.CanKeycardAuthenticationDevice))
|
||||
{
|
||||
SetScreen(AnnouncementScreen);
|
||||
HomeButton.Visible = false;
|
||||
}
|
||||
|
||||
// Station
|
||||
StationHeaderLabel.Visible = console.Comp.CanAnnounce || console.Comp.CanConfigureScreens || console.Comp.CanAlertLevel;
|
||||
AnnouncementTab.Visible = console.Comp.CanAnnounce;
|
||||
ScreenTab.Visible = console.Comp.CanConfigureScreens;
|
||||
AlertLevelTab.Visible = console.Comp.CanAlertLevel;
|
||||
|
||||
// Shuttles
|
||||
TransitHeaderLabel.Visible = console.Comp.CanCallShuttles; // Should be only hidden if all buttons under are also hidden.
|
||||
ShuttlesTab.Visible = console.Comp.CanCallShuttles;
|
||||
|
||||
// Emergency
|
||||
EmergencyHeaderLabel.Visible = console.Comp.CanKeycardAuthenticationDevice; // Should be only hidden if all buttons under are also hidden.
|
||||
KeycardAuthenticationDeviceTab.Visible = console.Comp.CanKeycardAuthenticationDevice;
|
||||
|
||||
ShuttleEmergencyButton.Disabled = !console.Comp.ShuttlesCallable;
|
||||
ShuttleExfiltrationButton.Disabled = !console.Comp.ShuttlesCallable;
|
||||
|
||||
ShuttleEmergencyButton.Text =
|
||||
Loc.GetString($"comms-console-menu-call-emergency.{console.Comp.ExpectedEvacuationArrival is null}");
|
||||
ShuttleExfiltrationButton.Text =
|
||||
Loc.GetString($"comms-console-menu-call-exfiltration.{console.Comp.ExpectedExfiltrationArrival is null}");
|
||||
|
||||
_alertLevels.RemoveAllChildren();
|
||||
foreach (var level in console.Comp.AlertLevels)
|
||||
{
|
||||
if (level.Id == console.Comp.CurrentAlertLevel)
|
||||
{
|
||||
AlertLevel.Text = Loc.GetString("comms-console-menu-current-alert-level",
|
||||
("name", Loc.GetString(level.AlertLevel)),
|
||||
("color", level.Color),
|
||||
("description", Loc.GetString(level.Description)));
|
||||
}
|
||||
|
||||
if (!level.CanSet)
|
||||
continue;
|
||||
|
||||
var btn = new Button
|
||||
{
|
||||
Text = Loc.GetString(level.AlertLevel),
|
||||
ToggleMode = true,
|
||||
Group = _group,
|
||||
Pressed = level.Id == console.Comp.CurrentAlertLevel,
|
||||
};
|
||||
_alertLevels.AddChild(btn);
|
||||
btn.OnPressed += _ =>
|
||||
{
|
||||
_alertLevelsPopup.Close();
|
||||
OnMessage?.Invoke(new DVCommunicationsConsoleAlertLevelMessage(level.Id));
|
||||
};
|
||||
}
|
||||
|
||||
if (console.Comp.LastConfiguredLine1 != _lastConfiguredLine1)
|
||||
ScreenLine1.Text = console.Comp.LastConfiguredLine1;
|
||||
|
||||
if (console.Comp.LastConfiguredLine2 != _lastConfiguredLine2)
|
||||
ScreenLine2.Text = console.Comp.LastConfiguredLine2;
|
||||
|
||||
ScreenContentsButton.SelectId((int)console.Comp.LastConfiguredContent);
|
||||
ScreenAlertBorder.Pressed = console.Comp.LastConfiguredShowBorders;
|
||||
|
||||
_lastConfiguredLine1 = console.Comp.LastConfiguredLine1;
|
||||
_lastConfiguredLine2 = console.Comp.LastConfiguredLine2;
|
||||
|
||||
UpdateKeycardAuthenticationDevice();
|
||||
}
|
||||
|
||||
private void UpdateKeycardAuthenticationDevice()
|
||||
{
|
||||
if (_user is not { } user || _stationKeycardAuthenticationDevice is not { } station)
|
||||
return;
|
||||
|
||||
var canCall = !_entity.HasComponent<DVStationKeycardAuthenticationDeviceAlreadySwipedComponent>(user)
|
||||
&& _entity.HasComponent<HumanoidProfileComponent>(user) // Can't be AI or a borg
|
||||
&& station.Comp.AccessibleAfter <= _timing.CurTime;
|
||||
|
||||
CallZeta.ForceDisabled = !(canCall && station.Comp.SwipingFor is null or DVStationKeycardAction.Mayday);
|
||||
CallZeta.Disabled = CallZeta.ForceDisabled;
|
||||
RequestCodes.ForceDisabled = !(canCall && station.Comp.SwipingFor is null or DVStationKeycardAction.Scuttling);
|
||||
RequestCodes.Disabled = RequestCodes.ForceDisabled;
|
||||
|
||||
if (CallZeta.IsConfirming || RequestCodes.IsConfirming)
|
||||
return;
|
||||
|
||||
// Kinda messy but it works
|
||||
if (station.Comp.SwipesStartedAt is { } firstSwipeTime)
|
||||
{
|
||||
var countdown = firstSwipeTime + station.Comp.SwipeWindow;
|
||||
var timeRemaining = countdown.Subtract(_timing.CurTime);
|
||||
if (timeRemaining > TimeSpan.Zero && station.Comp.SwipingFor is DVStationKeycardAction.Mayday)
|
||||
CallZeta.Text = $"{timeRemaining:ss\\.fff}";
|
||||
|
||||
if (timeRemaining > TimeSpan.Zero && station.Comp.SwipingFor is DVStationKeycardAction.Scuttling)
|
||||
RequestCodes.Text = $"{timeRemaining:ss\\.fff}";
|
||||
}
|
||||
else
|
||||
{
|
||||
CallZeta.Text = Loc.GetString("comms-console-menu-declare-mayday.button");
|
||||
RequestCodes.Text = Loc.GetString("comms-console-menu-request-codes.button");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAnnouncePressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
OnMessage?.Invoke(new DVCommunicationsConsoleAnnouncementMessage(Rope.Collapse(AnnounceInput.TextRope)));
|
||||
}
|
||||
|
||||
private void UpdateScreens()
|
||||
{
|
||||
OnMessage?.Invoke(new DVCommunicationsConsoleScreenConfigurationMessage(
|
||||
(DVScreenContent) ScreenContentsButton.SelectedId,
|
||||
ScreenAlertBorder.Pressed,
|
||||
ScreenLine1.Text,
|
||||
ScreenLine2.Text));
|
||||
}
|
||||
|
||||
private void OnUpdateScreenText(BaseButton.ButtonEventArgs buttonEventArgs)
|
||||
{
|
||||
UpdateScreens();
|
||||
}
|
||||
|
||||
private void OnScreenContentsSelected(OptionButton.ItemSelectedEventArgs itemSelectedEventArgs)
|
||||
{
|
||||
ScreenContentsButton.SelectId(itemSelectedEventArgs.Id);
|
||||
UpdateScreens();
|
||||
}
|
||||
|
||||
private void OnScreenAlertBorderToggled(BaseButton.ButtonToggledEventArgs buttonToggledEventArgs)
|
||||
{
|
||||
UpdateScreens();
|
||||
}
|
||||
|
||||
private void OnExfiltrationPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
OnMessage?.Invoke(new DVCommunicationsConsoleExfiltrationShuttleMessage(_console?.Comp.ExpectedExfiltrationArrival is null));
|
||||
}
|
||||
|
||||
private void OnEmergencyPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
OnMessage?.Invoke(new DVCommunicationsConsoleEvacuationShuttleMessage(_console?.Comp.ExpectedEvacuationArrival is null));
|
||||
}
|
||||
|
||||
private void OnCallZetaPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
OnMessage?.Invoke(new DVCommunicationsConsoleKeycardAuthenticationDeviceMessage(DVStationKeycardAction.Mayday));
|
||||
}
|
||||
|
||||
private void OnRequestCodesPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
OnMessage?.Invoke(new DVCommunicationsConsoleKeycardAuthenticationDeviceMessage(DVStationKeycardAction.Scuttling));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Content.Shared._DV.Communications;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client._DV.Communications;
|
||||
|
||||
public sealed class DVCommunicationsConsoleSystem : SharedDVCommunicationsConsoleSystem
|
||||
{
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DVCommunicationsConsoleComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<DVCommunicationsConsoleComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (!_userInterface.TryGetOpenUi<DVCommunicationsConsoleBoundUserInterface>(ent.Owner,
|
||||
DVCommunicationsConsoleUi.Key,
|
||||
out var bui))
|
||||
return;
|
||||
|
||||
bui.Update(ent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using Content.Shared._DV.KeycardAuthenticationDevice;
|
||||
|
||||
namespace Content.Client._DV.KeycardAuthenticationDevice;
|
||||
|
||||
public sealed class DVStationKeycardAuthenticationDeviceSystem : SharedDVStationKeycardAuthenticationDeviceSystem;
|
||||
@@ -0,0 +1,104 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Graphics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._DV.Overlays;
|
||||
|
||||
/// <summary>
|
||||
/// Makes darkness visible, and bright lights painfully visible
|
||||
/// Tweakable. Algo is max((light*gain)^exp, lightFloor)
|
||||
/// </summary>
|
||||
public sealed class DarkVisionOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowWorld;
|
||||
|
||||
private readonly ProtoId<ShaderPrototype> _shaderProto = "DarkVision";
|
||||
|
||||
public float LightFloor = 0.5f;
|
||||
public float LightGain = 2f;
|
||||
public float LightExp = 1f;
|
||||
|
||||
private readonly ShaderInstance _copyShader;
|
||||
private readonly ShaderInstance _remapShader;
|
||||
private readonly OverlayResourceCache<CachedResources> _resources = new();
|
||||
|
||||
public DarkVisionOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var proto = _prototype.Index<ShaderPrototype>(_shaderProto);
|
||||
_remapShader = proto.InstanceUnique();
|
||||
// With floor 0, gain 1, exp 1 the shader is an exact blend-mode-none copy.
|
||||
_copyShader = proto.InstanceUnique();
|
||||
_copyShader.SetParameter("lightFloor", 0f);
|
||||
_copyShader.SetParameter("lightGain", 1f);
|
||||
_copyShader.SetParameter("lightExp", 1f);
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var viewport = args.Viewport;
|
||||
var worldHandle = args.WorldHandle;
|
||||
|
||||
if (viewport.Eye == null)
|
||||
return;
|
||||
|
||||
var lightTarget = viewport.LightRenderTarget;
|
||||
var res = _resources.GetForViewport(viewport, static _ => new CachedResources());
|
||||
|
||||
if (res.ScratchTarget?.Size != lightTarget.Size)
|
||||
{
|
||||
res.ScratchTarget?.Dispose();
|
||||
res.ScratchTarget = _clyde.CreateLightRenderTarget(lightTarget.Size, "darkvision-scratch", depthStencil: false);
|
||||
}
|
||||
|
||||
var bounds = args.WorldBounds;
|
||||
var lightScale = lightTarget.Size / (Vector2) viewport.Size;
|
||||
var scale = viewport.RenderScale / (Vector2.One / lightScale);
|
||||
var localMatrix = lightTarget.GetWorldToLocalMatrix(viewport.Eye, scale);
|
||||
|
||||
// Copy the light buffer aside first: a texture can't be sampled while it is also the
|
||||
// render target being drawn into.
|
||||
worldHandle.RenderInRenderTarget(res.ScratchTarget, () =>
|
||||
{
|
||||
worldHandle.UseShader(_copyShader);
|
||||
worldHandle.SetTransform(localMatrix);
|
||||
worldHandle.DrawTextureRect(lightTarget.Texture, bounds);
|
||||
worldHandle.UseShader(null);
|
||||
}, Color.Black);
|
||||
|
||||
// Then write it back through the remap.
|
||||
_remapShader.SetParameter("lightFloor", LightFloor);
|
||||
_remapShader.SetParameter("lightGain", LightGain);
|
||||
_remapShader.SetParameter("lightExp", LightExp);
|
||||
worldHandle.RenderInRenderTarget(lightTarget, () =>
|
||||
{
|
||||
worldHandle.UseShader(_remapShader);
|
||||
worldHandle.SetTransform(localMatrix);
|
||||
worldHandle.DrawTextureRect(res.ScratchTarget.Texture, bounds);
|
||||
worldHandle.UseShader(null);
|
||||
}, null);
|
||||
}
|
||||
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
_resources.Dispose();
|
||||
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
private sealed class CachedResources : IDisposable
|
||||
{
|
||||
public IRenderTexture? ScratchTarget;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ScratchTarget?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using Content.Shared._DV.Overlays.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client._DV.Overlays;
|
||||
|
||||
public sealed class DarkVisionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _playerMan = default!;
|
||||
|
||||
private DarkVisionOverlay _overlay = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DarkVisionComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<DarkVisionComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<DarkVisionComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<DarkVisionComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
|
||||
_overlay = new();
|
||||
}
|
||||
|
||||
private void OnInit(Entity<DarkVisionComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
if (ent.Owner == _playerMan.LocalEntity)
|
||||
EnableOverlay(ent.Comp);
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<DarkVisionComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (ent.Owner == _playerMan.LocalEntity)
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(Entity<DarkVisionComponent> ent, ref LocalPlayerAttachedEvent args)
|
||||
{
|
||||
EnableOverlay(ent.Comp);
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(Entity<DarkVisionComponent> ent, ref LocalPlayerDetachedEvent args)
|
||||
{
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
}
|
||||
|
||||
private void EnableOverlay(DarkVisionComponent comp)
|
||||
{
|
||||
_overlay.LightFloor = comp.LightFloor;
|
||||
_overlay.LightGain = comp.LightGain;
|
||||
_overlay.LightExp = comp.LightExp;
|
||||
if (!_overlayMan.HasOverlay<DarkVisionOverlay>())
|
||||
_overlayMan.AddOverlay(_overlay);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System.Globalization;
|
||||
using Content.Client.GameTicking.Managers;
|
||||
using Content.Client.Interactable.Components;
|
||||
using Content.Client.TextScreen;
|
||||
using Content.Shared._DV.Screens;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._DV.Screens;
|
||||
|
||||
public sealed class DVScreenSystem : DVSharedScreenSystem
|
||||
{
|
||||
[Dependency] private readonly DVTextVisualsSystem _textVisuals = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly ClientGameTicker _ticker = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DVScreenComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<DVScreenComponent, AfterAutoHandleStateEvent>(OnScreenState);
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<DVScreenComponent>();
|
||||
while (query.MoveNext(out var uid, out var screen))
|
||||
{
|
||||
switch (screen.Content)
|
||||
{
|
||||
case DVScreenContent.Text:
|
||||
break;
|
||||
case DVScreenContent.CurrentTime:
|
||||
CurrentTime((uid, screen));
|
||||
break;
|
||||
case DVScreenContent.EstimatedTimeOfArrival:
|
||||
EstimatedTimeOfArrival((uid, screen));
|
||||
break;
|
||||
case DVScreenContent.AlertLevel:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInit(Entity<DVScreenComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
// Remove interaction outline because it outlines the offscreen text and looks bad
|
||||
RemComp<InteractionOutlineComponent>(ent);
|
||||
}
|
||||
|
||||
private void OnScreenState(Entity<DVScreenComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateVisuals(ent);
|
||||
|
||||
switch (ent.Comp.Content)
|
||||
{
|
||||
case DVScreenContent.Text:
|
||||
Text(ent);
|
||||
break;
|
||||
case DVScreenContent.CurrentTime:
|
||||
CurrentTime(ent);
|
||||
break;
|
||||
case DVScreenContent.EstimatedTimeOfArrival:
|
||||
EstimatedTimeOfArrival(ent);
|
||||
break;
|
||||
case DVScreenContent.AlertLevel:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void Text(Entity<DVScreenComponent> ent)
|
||||
{
|
||||
_textVisuals.SetText(ent.Owner, ent.Comp.Line1, ent.Comp.Line2);
|
||||
}
|
||||
|
||||
private void CurrentTime(Entity<DVScreenComponent> ent)
|
||||
{
|
||||
var time = (_timing.CurTime - _ticker.RoundStartTimeSpan).Duration();
|
||||
_textVisuals.SetText(ent.Owner, Loc.GetString("status-display-time"), time.ToString("hh\\:mm"));
|
||||
}
|
||||
|
||||
private void EstimatedTimeOfArrival(Entity<DVScreenComponent> ent)
|
||||
{
|
||||
if (ent.Comp.TargetTime <= _timing.CurTime)
|
||||
{
|
||||
_textVisuals.SetText(ent.Owner, string.Empty, string.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
var time = (_timing.CurTime - ent.Comp.TargetTime).Duration();
|
||||
var formatted = time.ToString("mm\\:ss");
|
||||
Log.Debug($"{_timing.CurTime} - {ent.Comp.TargetTime} = {time}");
|
||||
var title = ent.Comp.ScreenIsAtDestination ? Loc.GetString("status-display-etd") : Loc.GetString("status-display-eta");
|
||||
|
||||
_textVisuals.SetText(ent.Owner, title, formatted);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
namespace Content.Client._DV.Screens;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class DVTextRenderingOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
private readonly SpriteSystem _sprite;
|
||||
private readonly AnimationPlayerSystem _animationPlayer;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpaceBelowWorld;
|
||||
|
||||
private readonly Queue<(Entity<DVTextVisualsComponent> Entity, Font Font, CancellationToken Cancellation)> _queue = new();
|
||||
|
||||
public const string MarqueeKey = "dv-text-screen-marquee";
|
||||
|
||||
public DVTextRenderingOverlay(SpriteSystem sprite, AnimationPlayerSystem animationPlayer)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_sprite = sprite;
|
||||
_animationPlayer = animationPlayer;
|
||||
|
||||
ZIndex = -100; // this needs to render before almost everything
|
||||
}
|
||||
|
||||
public CancellationTokenSource QueueRender(
|
||||
Entity<DVTextVisualsComponent> ent,
|
||||
Font font)
|
||||
{
|
||||
var source = new CancellationTokenSource();
|
||||
_queue.Enqueue((ent, font, source.Token));
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var screenHandle = args.ScreenHandle;
|
||||
|
||||
while (_queue.TryDequeue(out var queued))
|
||||
{
|
||||
if (queued.Cancellation.IsCancellationRequested)
|
||||
continue;
|
||||
|
||||
var font = queued.Font;
|
||||
foreach (var row in queued.Entity.Comp.Rows)
|
||||
{
|
||||
if (row.Text == string.Empty)
|
||||
{
|
||||
_sprite.LayerSetTexture(queued.Entity.Owner, row.Layer, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
var dimensions = screenHandle.GetDimensions(queued.Font, row.Text, 1f);
|
||||
var dimensionsInt = new Vector2i((int)MathF.Round(dimensions.X), (int)MathF.Round(dimensions.Y));
|
||||
|
||||
if (row.Texture is null || row.Texture.Size != dimensionsInt)
|
||||
{
|
||||
row.Texture = _clyde.CreateRenderTarget(dimensionsInt,
|
||||
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8),
|
||||
name: $"dv-text-visuals-{queued.Entity.Owner.Id}");
|
||||
|
||||
_sprite.LayerSetTexture(queued.Entity.Owner, row.Layer, row.Texture.Texture);
|
||||
_sprite.LayerSetOffset(queued.Entity.Owner, row.Layer, row.Offset);
|
||||
}
|
||||
|
||||
args.DrawingHandle.RenderInRenderTarget(row.Texture,
|
||||
() =>
|
||||
{
|
||||
screenHandle.DrawString(font, Vector2.Zero, row.Text);
|
||||
},
|
||||
Color.Transparent);
|
||||
}
|
||||
|
||||
_animationPlayer.Stop(queued.Entity.Owner, MarqueeKey);
|
||||
if (CreateMarqueeAnimation(queued.Entity) is { } animation)
|
||||
{
|
||||
queued.Entity.Comp.Animation = animation;
|
||||
_animationPlayer.Play(queued.Entity.Owner, animation, MarqueeKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Animation? CreateMarqueeAnimation(Entity<DVTextVisualsComponent> ent)
|
||||
{
|
||||
var largestRowWidth = ent.Comp.Rows.Aggregate(0, (i, row) => Math.Max(i, row.Texture?.Size.X ?? 0));
|
||||
var animationTime = ent.Comp.MarqueeRate * largestRowWidth;
|
||||
var marqueeWidth = new Vector2((float)ent.Comp.MarqueeWidth / EyeManager.PixelsPerMeter, 0);
|
||||
|
||||
var animation = new Animation
|
||||
{
|
||||
Length = animationTime,
|
||||
};
|
||||
|
||||
foreach (var row in ent.Comp.Rows)
|
||||
{
|
||||
if (row.Texture is null)
|
||||
continue;
|
||||
|
||||
var rowHalfWidth = new Vector2(row.Texture.Size.X / 2f / EyeManager.PixelsPerMeter, 0f);
|
||||
|
||||
if (row.Texture.Size.X <= ent.Comp.MarqueeWidth)
|
||||
continue;
|
||||
|
||||
animation.AnimationTracks.Add(new AnimationTrackLayerOffset()
|
||||
{
|
||||
Layer = row.Layer,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(row.Offset + rowHalfWidth + marqueeWidth, 0f),
|
||||
new AnimationTrackProperty.KeyFrame(row.Offset - rowHalfWidth - marqueeWidth, (float)animationTime.TotalSeconds),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return animation.AnimationTracks.Count > 0 ? animation : null;
|
||||
}
|
||||
|
||||
public sealed class AnimationTrackLayerOffset : AnimationTrackProperty
|
||||
{
|
||||
public required Enum Layer;
|
||||
private readonly SpriteSystem _sprite = IoCManager.Resolve<IEntityManager>().System<SpriteSystem>();
|
||||
|
||||
protected override void ApplyProperty(object context, object value)
|
||||
{
|
||||
if (value is not Vector2 vector)
|
||||
throw new InvalidOperationException("Value must be a .");
|
||||
|
||||
_sprite.LayerSetOffset((EntityUid) context, Layer, vector);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using Content.Shared._DV.Screens;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Content.Client._DV.Screens;
|
||||
|
||||
[RegisterComponent]
|
||||
[Access(typeof(DVTextVisualsSystem), typeof(DVTextRenderingOverlay))]
|
||||
public sealed partial class DVTextVisualsComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public List<DVTextVisualsRow> Rows;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan MarqueeRate = TimeSpan.FromSeconds(0.045f);
|
||||
|
||||
[DataField]
|
||||
public int MarqueeWidth = 24;
|
||||
|
||||
[DataField]
|
||||
public int MarqueePadding = 8;
|
||||
|
||||
public Animation? Animation;
|
||||
|
||||
public CancellationTokenSource? Token;
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
public sealed partial class DVTextVisualsRow
|
||||
{
|
||||
public IRenderTexture? Texture;
|
||||
|
||||
[DataField]
|
||||
public string Text;
|
||||
|
||||
[DataField]
|
||||
public Vector2 Offset;
|
||||
|
||||
[DataField(required: true)]
|
||||
public Enum Layer = DVTextScreenVisualLayers.Line1;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
|
||||
namespace Content.Client._DV.Screens;
|
||||
|
||||
public sealed class DVTextVisualsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
[Dependency] private readonly IResourceCache _resource = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
|
||||
|
||||
private DVTextRenderingOverlay _textRendering = default!;
|
||||
|
||||
private Font _font = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_textRendering = new(_sprite, _animationPlayer);
|
||||
_overlay.AddOverlay(_textRendering);
|
||||
_font = new VectorFont(_resource.GetResource<FontResource>("/Fonts/_DV/TinyUnicode.ttf"), 12);
|
||||
|
||||
SubscribeLocalEvent<DVTextVisualsComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<DVTextVisualsComponent, ComponentShutdown>(OnComponentShutdown);
|
||||
|
||||
SubscribeLocalEvent<DVTextVisualsComponent, AnimationCompletedEvent>(OnAnimationComplete);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_overlay.RemoveOverlay(_textRendering);
|
||||
}
|
||||
|
||||
private void OnComponentInit(Entity<DVTextVisualsComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
ent.Comp.Token = _textRendering.QueueRender(ent, _font);
|
||||
}
|
||||
|
||||
private void OnComponentShutdown(Entity<DVTextVisualsComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
foreach (var row in ent.Comp.Rows)
|
||||
{
|
||||
row.Texture?.Dispose();
|
||||
}
|
||||
ent.Comp.Token?.Cancel();
|
||||
}
|
||||
|
||||
private void OnAnimationComplete(Entity<DVTextVisualsComponent> ent, ref AnimationCompletedEvent args)
|
||||
{
|
||||
if (args.Key != DVTextRenderingOverlay.MarqueeKey || !args.Finished || ent.Comp.Animation is not { } animation)
|
||||
return;
|
||||
|
||||
_animationPlayer.Play(ent.Owner, animation, DVTextRenderingOverlay.MarqueeKey);
|
||||
}
|
||||
|
||||
public void SetText(Entity<DVTextVisualsComponent?> ent, params string[] rows)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return;
|
||||
|
||||
var count = Math.Min(rows.Length, ent.Comp.Rows.Count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
ent.Comp.Rows[i].Text = rows[i];
|
||||
}
|
||||
|
||||
ent.Comp.Token?.Cancel();
|
||||
ent.Comp.Token = _textRendering.QueueRender((ent, ent.Comp), _font);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using static Content.Client.Stylesheets.StylesheetHelpers;
|
||||
|
||||
namespace Content.Client._DV.Stylesheets.Sheetlets;
|
||||
|
||||
[CommonSheetlet]
|
||||
public sealed class DVLineEditSheetlet : Sheetlet<PalettedStylesheet>
|
||||
{
|
||||
public override StyleRule[] GetRules(PalettedStylesheet sheet, object config)
|
||||
{
|
||||
var tinyUnicode = ResCache.GetFont("/Fonts/_DV/TinyUnicode.ttf", size: 24);
|
||||
|
||||
return
|
||||
[
|
||||
E<LineEdit>()
|
||||
.Class("comms-console-display")
|
||||
.Font(tinyUnicode),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared._DV.Administration.Components; // DeltaV - Unorbitable
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -6,7 +7,7 @@ using Robust.Shared.Utility;
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Stealth)]
|
||||
public sealed class StealthminCommand : LocalizedCommands
|
||||
public sealed class StealthminCommand : LocalizedEntityCommands // DeltaV - Unorbitable, LocalizedCommands to LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
|
||||
@@ -26,8 +27,20 @@ public sealed class StealthminCommand : LocalizedCommands
|
||||
DebugTools.AssertNotNull(adminData);
|
||||
|
||||
if (!adminData!.Stealth)
|
||||
{
|
||||
_adminManager.Stealth(player);
|
||||
// DeltaV - Unorbitable START
|
||||
if (player.AttachedEntity is { } attachedEntity)
|
||||
EntityManager.EnsureComponent<UnorbitableComponent>(attachedEntity);
|
||||
// DeltaV END
|
||||
}
|
||||
else
|
||||
{
|
||||
_adminManager.UnStealth(player);
|
||||
// DeltaV - Unorbitable START
|
||||
if (player.AttachedEntity is { } attachedEntity)
|
||||
EntityManager.RemoveComponent<UnorbitableComponent>(attachedEntity);
|
||||
// DeltaV END
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Ninja.Systems;
|
||||
using Content.Shared._DV.Communications; // DeltaV Comms Console
|
||||
using Content.Shared.Communications;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Interaction;
|
||||
@@ -35,7 +36,7 @@ public sealed class CommsHackerSystem : SharedCommsHackerSystem
|
||||
/// </summary>
|
||||
private void OnBeforeInteractHand(EntityUid uid, CommsHackerComponent comp, BeforeInteractHandEvent args)
|
||||
{
|
||||
if (args.Handled || !HasComp<CommunicationsConsoleComponent>(args.Target))
|
||||
if (args.Handled || !HasComp<DVCommunicationsConsoleComponent>(args.Target)) // DeltaV - Comms Console
|
||||
return;
|
||||
|
||||
// TODO: generic check event
|
||||
|
||||
@@ -23,7 +23,7 @@ using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Server.Communications
|
||||
{
|
||||
public sealed partial class CommunicationsConsoleSystem : EntitySystem // DeltaV - Partial Class
|
||||
public sealed class CommunicationsConsoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
|
||||
[Dependency] private readonly AlertLevelSystem _alertLevelSystem = default!;
|
||||
@@ -53,8 +53,6 @@ namespace Content.Server.Communications
|
||||
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleCallEmergencyShuttleMessage>(OnCallShuttleMessage);
|
||||
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleRecallEmergencyShuttleMessage>(OnRecallShuttleMessage);
|
||||
|
||||
InitializeExfiltration(); // DeltaV - Exfiltration shuttle
|
||||
|
||||
// On console init, set cooldown
|
||||
SubscribeLocalEvent<CommunicationsConsoleComponent, MapInitEvent>(OnCommunicationsConsoleMapInit);
|
||||
}
|
||||
@@ -138,7 +136,6 @@ namespace Content.Server.Communications
|
||||
List<string>? levels = null;
|
||||
string currentLevel = default!;
|
||||
float currentDelay = 0;
|
||||
TimeSpan? exfiltrationTime = null; // DeltaV - exfiltration shuttle
|
||||
|
||||
if (stationUid != null)
|
||||
{
|
||||
@@ -160,12 +157,6 @@ namespace Content.Server.Communications
|
||||
currentLevel = alertComp.CurrentLevel;
|
||||
currentDelay = _alertLevelSystem.GetAlertLevelDelay(stationUid.Value, alertComp);
|
||||
}
|
||||
// Begin DeltaV - exfiltration shuttle
|
||||
if (TryComp<StationExfiltrationComponent>(stationUid, out var exfiltration))
|
||||
{
|
||||
exfiltrationTime = exfiltration.ArrivalTime;
|
||||
}
|
||||
// End DeltaV - exfiltration shuttle
|
||||
}
|
||||
|
||||
_uiSystem.SetUiState(uid, CommunicationsConsoleUiKey.Key, new CommunicationsConsoleInterfaceState(
|
||||
@@ -174,8 +165,7 @@ namespace Content.Server.Communications
|
||||
levels,
|
||||
currentLevel,
|
||||
currentDelay,
|
||||
_roundEndSystem.ExpectedCountdownEnd,
|
||||
exfiltrationTime // DeltaV - exfiltration shuttle
|
||||
_roundEndSystem.ExpectedCountdownEnd
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Server.RoundEnd; // DeltaV
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
@@ -43,4 +44,16 @@ public sealed partial class XenoborgsRuleComponent : Component
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool XenoborgShuttleCalled = false;
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - The behavior of the round if the mothership core is deleted.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public RoundEndBehavior XenoborgRoundEndBehavior = RoundEndBehavior.BecomeSurvival;
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - The amount of time before the evac shuttle will arrive if the XenoborgRoundEndBehavior is set to ShuttleCall.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan XenoborgShuttleDelay = TimeSpan.FromMinutes(10);
|
||||
}
|
||||
|
||||
@@ -30,10 +30,20 @@ public sealed partial class ZombieRuleComponent : Component
|
||||
/// <summary>
|
||||
/// DeltaV - The behavior of the round if all zombies are defeated.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public RoundEndBehavior ZombieRoundEndBehavior = RoundEndBehavior.BecomeSurvival;
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - The amount of time before the evac shuttle will arrive if the ZombieRoundEndBehavior is set to ShuttleCall.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan ZombieShuttleDelay = TimeSpan.FromMinutes(10);
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - If true, ZombieRuleSystem will do the ZombieRoundEndBehavior if zombies are defeated.
|
||||
/// This is needed because ZombieOutbreak uses the ZombieRuleComponent but the system won't know if
|
||||
/// it was roundstart or not.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool IsRoundStartZombies = false;
|
||||
}
|
||||
|
||||
@@ -520,18 +520,17 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
|
||||
// BEGIN DeltaV - Detect Nukie Failure Better
|
||||
// We need to use a EntityQueryEnumerator instead of a EntityQuery, because we need the uid to check for cuffs
|
||||
var operatives = new List<Entity<NukeOperativeComponent, MobStateComponent, TransformComponent>>();
|
||||
var operativesEnumerator = EntityQueryEnumerator<NukeOperativeComponent, MobStateComponent, TransformComponent>();
|
||||
while (operativesEnumerator.MoveNext(out var uid, out var nukeOp, out var mobState, out var transform))
|
||||
var operatives = new List<Entity<NukeOperativeComponent, MobStateComponent>>();
|
||||
var operativesEnumerator = EntityQueryEnumerator<NukeOperativeComponent, MobStateComponent>();
|
||||
while (operativesEnumerator.MoveNext(out var uid, out var nukeOp, out var mobState))
|
||||
{
|
||||
operatives.Add((uid, nukeOp, mobState, transform));
|
||||
operatives.Add((uid, nukeOp, mobState));
|
||||
}
|
||||
|
||||
var operativesAlive = operatives
|
||||
.Where(op => op.Comp3.MapID == shuttleMapId
|
||||
|| op.Comp3.MapID == targetStationMap)
|
||||
.Any(op => op.Comp2.CurrentState == MobState.Alive && op.Comp1.Running &&
|
||||
!TryComp<CuffableComponent>(op, out _)); // in the case the crew keeps one alive
|
||||
.Any(op => op.Comp1.Running
|
||||
&& op.Comp2.CurrentState == MobState.Alive
|
||||
&& TryComp<CuffableComponent>(op, out var cuffable) && cuffable.CuffedHandCount == 0); // in the case the crew keeps one alive
|
||||
// END DeltaV
|
||||
|
||||
if (operativesAlive)
|
||||
|
||||
@@ -42,6 +42,14 @@ public sealed class XenoborgsRuleSystem : GameRuleSystem<XenoborgsRuleComponent>
|
||||
colorOverride: AnnouncmentColor);
|
||||
|
||||
ent.Comp.MothershipCoreDeathAnnouncmentSent = true;
|
||||
|
||||
// BEGIN DeltaV - Convert to Survival
|
||||
if (ent.Comp.XenoborgRoundEndBehavior != RoundEndBehavior.Nothing)
|
||||
{
|
||||
_roundEnd.DoRoundEndBehavior(ent.Comp.XenoborgRoundEndBehavior, ent.Comp.XenoborgShuttleDelay);
|
||||
ent.Comp.XenoborgRoundEndBehavior = RoundEndBehavior.Nothing;
|
||||
}
|
||||
// END DeltaV
|
||||
}
|
||||
|
||||
// TODO: Refactor the end of round text
|
||||
|
||||
@@ -115,29 +115,30 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
||||
/// </summary>
|
||||
private void CheckRoundEnd(ZombieRuleComponent zombieRuleComponent)
|
||||
{
|
||||
// BEGIN DeltaV - Change mode to survival if zombies die
|
||||
// Zombies have failed to launch and the mode has been switched to survival
|
||||
if (zombieRuleComponent.ZombieRoundEndBehavior == RoundEndBehavior.Nothing)
|
||||
return;
|
||||
// END DeltaV
|
||||
|
||||
var healthy = GetHealthyHumans();
|
||||
if (healthy.Count == 1) // Only one human left. spooky
|
||||
_popup.PopupEntity(Loc.GetString("zombie-alone"), healthy[0], healthy[0]);
|
||||
|
||||
|
||||
// BEGIN DeltaV - Change mode to survival if zombies die
|
||||
var anyInitialInfectedAlive = EntityQuery<InitialInfectedComponent, MobStateComponent>(false)
|
||||
.Any(x => x.Item2.CurrentState != MobState.Dead);
|
||||
var anyLivingZombies = EntityQuery<ZombieComponent, MobStateComponent>(false)
|
||||
.Any(x => x.Item2.CurrentState != MobState.Dead);
|
||||
var anyPendingZombies = EntityQuery<PendingZombieComponent>(false).Any();
|
||||
|
||||
// All II turned/dead, all entities that have turned are dead, and there are no more pending zombies
|
||||
if (!anyInitialInfectedAlive && !anyLivingZombies && !anyPendingZombies)
|
||||
if (zombieRuleComponent.IsRoundStartZombies)
|
||||
{
|
||||
_roundEnd.DoRoundEndBehavior(zombieRuleComponent.ZombieRoundEndBehavior, zombieRuleComponent.ZombieShuttleDelay);
|
||||
zombieRuleComponent.ZombieRoundEndBehavior = RoundEndBehavior.Nothing; // stop this check in the future
|
||||
// Zombies have failed to launch and the mode has been switched to survival
|
||||
if (zombieRuleComponent.ZombieRoundEndBehavior == RoundEndBehavior.Nothing)
|
||||
return;
|
||||
|
||||
// BEGIN DeltaV - Change mode to survival if zombies die
|
||||
var anyInitialInfectedAlive = EntityQuery<InitialInfectedComponent, MobStateComponent>(false)
|
||||
.Any(x => x.Item2.CurrentState != MobState.Dead);
|
||||
var anyLivingZombies = EntityQuery<ZombieComponent, MobStateComponent>(false)
|
||||
.Any(x => x.Item2.CurrentState != MobState.Dead);
|
||||
var anyPendingZombies = EntityQuery<PendingZombieComponent>(false).Any();
|
||||
|
||||
// All II turned/dead, all entities that have turned are dead, and there are no more pending zombies
|
||||
if (!anyInitialInfectedAlive && !anyLivingZombies && !anyPendingZombies)
|
||||
{
|
||||
_roundEnd.DoRoundEndBehavior(zombieRuleComponent.ZombieRoundEndBehavior, zombieRuleComponent.ZombieShuttleDelay);
|
||||
zombieRuleComponent.ZombieRoundEndBehavior = RoundEndBehavior.Nothing; // stop this check in the future
|
||||
}
|
||||
}
|
||||
// END DeltaV
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.NameModifier.EntitySystems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Server.Preferences.Managers; // DeltaV
|
||||
using Content.Shared._DV.Administration.EntitySystems; // DeltaV - Unorbitable
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Warps;
|
||||
@@ -75,6 +76,7 @@ namespace Content.Server.Ghost
|
||||
[Dependency] private readonly GhostSpriteStateSystem _ghostState = default!;
|
||||
[Dependency] private readonly IServerPreferencesManager _preferencesManager = default!; // DeltaV
|
||||
[Dependency] private readonly IAdminManager _admin = default!; // DeltaV
|
||||
[Dependency] private readonly UnorbitableSystem _unorbitable = default!; // DeltaV - Unorbitable
|
||||
|
||||
private EntityQuery<GhostComponent> _ghostQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
@@ -341,6 +343,11 @@ namespace Content.Server.Ghost
|
||||
return;
|
||||
}
|
||||
|
||||
// DeltaV - Unorbitable START
|
||||
if (!_unorbitable.CanFollow(attached, realTarget))
|
||||
return;
|
||||
// DeltaV END
|
||||
|
||||
WarpTo(attached, realTarget);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,17 @@ public sealed partial class EmergencyLightComponent : SharedEmergencyLightCompon
|
||||
[DataField("chargingEfficiency")]
|
||||
public float ChargingEfficiency = 0.85f;
|
||||
|
||||
// Begin DeltaV Additions
|
||||
[DataField]
|
||||
public float MaydayRadius = 10f;
|
||||
|
||||
[DataField]
|
||||
public float MaydayFalloff = 0f;
|
||||
|
||||
[DataField]
|
||||
public float MaydayEnergy = 0.7f;
|
||||
// End DeltaV Additions
|
||||
|
||||
public Dictionary<EmergencyLightState, string> BatteryStateText = new()
|
||||
{
|
||||
{ EmergencyLightState.Full, "emergency-light-component-light-state-full" },
|
||||
|
||||
@@ -85,11 +85,11 @@ public sealed partial class HealthAnalyzerSystem : EntitySystem // DeltaV - Made
|
||||
if (component.MaxScanRange != null && !_transformSystem.InRange(patientCoordinates, transform.Coordinates, component.MaxScanRange.Value))
|
||||
{
|
||||
//Range too far, disable updates
|
||||
PauseAnalyzingEntity((uid, component), patient); // DeltaV - Analyzer Reactivation
|
||||
PauseAnalyzingEntity((uid, component), patient);
|
||||
continue;
|
||||
}
|
||||
|
||||
component.IsAnalyzerActive = true; // DeltaV - Analyzer Reactivation
|
||||
component.IsAnalyzerActive = true;
|
||||
UpdateScannedUser(uid, patient, true);
|
||||
}
|
||||
}
|
||||
@@ -197,7 +197,7 @@ public sealed partial class HealthAnalyzerSystem : EntitySystem // DeltaV - Made
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - If the scanner is active, sends one last update and sets it to inactive.
|
||||
/// If the scanner is active, sends one last update and sets it to inactive.
|
||||
/// </summary>
|
||||
/// <param name="healthAnalyzer">The health analyzer that's receiving the updates</param>
|
||||
/// <param name="target">The entity to analyze</param>
|
||||
|
||||
@@ -22,6 +22,7 @@ using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Store.Components;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Weapons.Melee.Events; // DeltaV
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -64,6 +65,7 @@ public sealed partial class RevenantSystem : EntitySystem
|
||||
SubscribeLocalEvent<RevenantComponent, StatusEffectAddedEvent>(OnStatusAdded);
|
||||
SubscribeLocalEvent<RevenantComponent, StatusEffectEndedEvent>(OnStatusEnded);
|
||||
SubscribeLocalEvent<RoundEndTextAppendEvent>(_ => MakeVisible(true));
|
||||
SubscribeLocalEvent<RevenantComponent, MeleeAttackEvent>(OnMeleeAttack); // DeltaV
|
||||
|
||||
SubscribeLocalEvent<RevenantComponent, GetVisMaskEvent>(OnRevenantGetVis);
|
||||
|
||||
@@ -131,6 +133,14 @@ public sealed partial class RevenantSystem : EntitySystem
|
||||
ChangeEssenceAmount(uid, essenceDamage, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - No more attack spam and remaining invisible
|
||||
/// </summary>
|
||||
private void OnMeleeAttack(Entity<RevenantComponent> ent, ref MeleeAttackEvent args)
|
||||
{
|
||||
_statusEffects.TryAddStatusEffect<CorporealComponent>(ent, "Corporeal", ent.Comp.MeleeRevealTime, false);
|
||||
}
|
||||
|
||||
public bool ChangeEssenceAmount(EntityUid uid, FixedPoint2 amount, RevenantComponent? component = null, bool allowDeath = true, bool regenCap = false)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
|
||||
@@ -41,7 +41,8 @@ namespace Content.Server.StationEvents
|
||||
if (TryComp<NextEventComponent>(uid, out var nextEventComponent)
|
||||
&& _event.TryGenerateRandomEvent(component.ScheduledGameRules, TimeSpan.FromSeconds(component.TimeUntilNextEvent)) is {} firstEvent)
|
||||
{
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", firstEvent), ("seconds", (int)component.TimeUntilNextEvent)));
|
||||
var nextEventTime = GameTicker.RoundDuration() + TimeSpan.FromSeconds(component.TimeUntilNextEvent);
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", firstEvent), ("time", nextEventTime.ToString(@"hh\:mm\:ss"))));
|
||||
_next.UpdateNextEvent(nextEventComponent, firstEvent, GameTicker.RoundDuration() + TimeSpan.FromSeconds(component.TimeUntilNextEvent));
|
||||
}
|
||||
// End DeltaV Additions
|
||||
@@ -81,7 +82,7 @@ namespace Content.Server.StationEvents
|
||||
if (_event.TryGenerateRandomEvent(eventScheduler.ScheduledGameRules, nextEventTime) is not {} generatedEvent)
|
||||
continue;
|
||||
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", generatedEvent), ("seconds", (int)eventScheduler.TimeUntilNextEvent)));
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", generatedEvent), ("time", nextEventTime.ToString(@"hh\:mm\:ss"))));
|
||||
// Cycle the stashed event with the new generated event and time.
|
||||
var storedEvent = _next.UpdateNextEvent(nextEventComponent, generatedEvent, nextEventTime);
|
||||
if (string.IsNullOrEmpty(storedEvent)) //If there was no stored event don't try to run it.
|
||||
|
||||
@@ -14,9 +14,12 @@ public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingS
|
||||
[Dependency] private readonly IChatManager _chatManager = default!; // DeltaV
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly EventManagerSystem _event = default!;
|
||||
#if DEBUG // DeltaV - Only used if built by Debug
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||
#endif
|
||||
[Dependency] private readonly NextEventSystem _next = default!; // DeltaV
|
||||
|
||||
[Dependency] private readonly IGameTiming _timing = default!; // DeltaV
|
||||
|
||||
/* DeltaV
|
||||
/// <summary>
|
||||
/// Returns the ChaosModifier which increases as round time increases to a point.
|
||||
@@ -50,7 +53,8 @@ public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingS
|
||||
if (TryComp<NextEventComponent>(uid, out var nextEventComponent)
|
||||
&& _event.TryGenerateRandomEvent(component.ScheduledGameRules, TimeSpan.FromSeconds(component.TimeUntilNextEvent)) is {} firstEvent)
|
||||
{
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", firstEvent), ("seconds", (int)component.TimeUntilNextEvent)));
|
||||
var nextEventTime = GameTicker.RoundDuration() + TimeSpan.FromSeconds(component.TimeUntilNextEvent);
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", firstEvent), ("time", nextEventTime.ToString(@"hh\:mm\:ss"))));
|
||||
_next.UpdateNextEvent(nextEventComponent, firstEvent, GameTicker.RoundDuration() + TimeSpan.FromSeconds(component.TimeUntilNextEvent));
|
||||
}
|
||||
// End DeltaV Additions: init NextEventComp
|
||||
@@ -83,7 +87,7 @@ public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingS
|
||||
if (_event.TryGenerateRandomEvent(scheduler.ScheduledGameRules, nextEventTime) is not {} generatedEvent)
|
||||
continue;
|
||||
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", generatedEvent), ("seconds", (int)scheduler.TimeUntilNextEvent)));
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", generatedEvent), ("time", nextEventTime.ToString(@"hh\:mm\:ss"))));
|
||||
// Cycle the stashed event with the new generated event and time.
|
||||
string? storedEvent = _next.UpdateNextEvent(nextEventComponent, generatedEvent, nextEventTime);
|
||||
if (string.IsNullOrEmpty(storedEvent)) //If there was no stored event don't try to run it.
|
||||
@@ -114,13 +118,28 @@ public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingS
|
||||
// Begin DeltaV Additions
|
||||
var averageTimeUntilNextEvent = component.TimeKeyPoints[0].Y;
|
||||
var timeUntilNextEventDeviation = _random.NextFloat(-1f, 1f) * component.TimeDeviation;
|
||||
var roundTime = (float)_gameTicker.RoundDuration().TotalMinutes;
|
||||
var absoluteTimePoint = 0f;
|
||||
|
||||
var ruleActivated = TimeSpan.FromSeconds(0);
|
||||
if (TryComp<GameRuleComponent>(uid, out var gameRule))
|
||||
{
|
||||
ruleActivated = gameRule.ActivatedAt;
|
||||
}
|
||||
|
||||
var ruleTime = _timing.CurTime.Subtract(ruleActivated);
|
||||
|
||||
#if DEBUG
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-debug-round-time", ("time", _gameTicker.RoundDuration())));
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-debug-ramping-time", ("time", ruleActivated)));
|
||||
// This will be what the keypoint system looks at to determine where to start ramping
|
||||
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-debug-keypoint-time", ("time", ruleTime)));
|
||||
_chatManager.SendAdminAlert("_______________"); // Easier to read
|
||||
#endif
|
||||
|
||||
var absoluteTimePoint = 0f;
|
||||
foreach (var point in component.TimeKeyPoints)
|
||||
{
|
||||
absoluteTimePoint += point.X;
|
||||
if (roundTime >= absoluteTimePoint)
|
||||
if (ruleTime.TotalMinutes >= absoluteTimePoint)
|
||||
averageTimeUntilNextEvent = point.Y;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
using Content.Server._DV.Station.Components;
|
||||
using Content.Server._DV.Station.Systems;
|
||||
using Content.Shared._DV.Communications;
|
||||
|
||||
namespace Content.Server.Communications;
|
||||
|
||||
public sealed partial class CommunicationsConsoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly StationExfiltrationSystem _stationExfiltration = default!;
|
||||
|
||||
private void InitializeExfiltration()
|
||||
{
|
||||
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleExfiltrationShuttleMessage>(OnExfiltrationMessage);
|
||||
}
|
||||
|
||||
private void OnExfiltrationMessage(Entity<CommunicationsConsoleComponent> ent, ref CommunicationsConsoleExfiltrationShuttleMessage args)
|
||||
{
|
||||
if (_stationSystem.GetOwningStation(ent) is not { } station)
|
||||
return;
|
||||
|
||||
if (!CanUse(args.Actor, ent))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("comms-console-permission-denied"), ent, args.Actor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Call)
|
||||
_stationExfiltration.Call(station);
|
||||
else
|
||||
_stationExfiltration.Recall(station);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
using Content.Server._DV.Station.Components;
|
||||
using Content.Server._DV.Station.Systems;
|
||||
using Content.Server.AlertLevel;
|
||||
using Content.Server.Communications;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Shared._DV.Communications;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Station;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._DV.Communications;
|
||||
|
||||
public sealed class DVCommunicationsConsoleSystem : SharedDVCommunicationsConsoleSystem
|
||||
{
|
||||
[Dependency] private readonly SharedStationSystem _station = default!;
|
||||
[Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
||||
[Dependency] private readonly AlertLevelSystem _alertLevel = default!;
|
||||
[Dependency] private readonly StationExfiltrationSystem _stationExfiltration = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RoundEndSystemChangedEvent>(OnRoundEndChanged);
|
||||
SubscribeLocalEvent<StationExfiltrationChangedEvent>(OnExfiltrationChanged);
|
||||
SubscribeLocalEvent<AlertLevelChangedEvent>(OnAlertLevelChanged);
|
||||
}
|
||||
|
||||
private void OnAlertLevelChanged(AlertLevelChangedEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<DVCommunicationsConsoleComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (_station.GetOwningStation(uid) != ev.Station)
|
||||
continue;
|
||||
|
||||
var alertLevel = Comp<AlertLevelComponent>(ev.Station);
|
||||
comp.CanSetAlertAt = Timing.CurTime + TimeSpan.FromSeconds(alertLevel.CurrentDelay);
|
||||
comp.CurrentAlertLevel = ev.AlertLevel;
|
||||
if (alertLevel.IsLevelLocked)
|
||||
comp.CanSetAlertAt = null;
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExfiltrationChanged(ref StationExfiltrationChangedEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<DVCommunicationsConsoleComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (_station.GetOwningStation(uid) != ev.Station)
|
||||
continue;
|
||||
|
||||
comp.ExpectedExfiltrationArrival = ev.Station.Comp.ArrivalTime;
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRoundEndChanged(RoundEndSystemChangedEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<DVCommunicationsConsoleComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
comp.ExpectedEvacuationArrival = _roundEnd.ExpectedCountdownEnd;
|
||||
comp.ExpectedEvacuationDuration = _roundEnd.ExpectedShuttleLength;
|
||||
comp.ShuttlesCallable = ShuttlesCallable();
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMapInit(Entity<DVCommunicationsConsoleComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
base.OnMapInit(ent, ref args);
|
||||
|
||||
if (_station.GetOwningStation(ent) is not { } station)
|
||||
return;
|
||||
|
||||
if (!TryComp<AlertLevelComponent>(station, out var alertLevel))
|
||||
return;
|
||||
|
||||
ent.Comp.CurrentAlertLevel = alertLevel.CurrentLevel;
|
||||
var proto = _prototype.Index<AlertLevelPrototype>(alertLevel.AlertLevelPrototype);
|
||||
foreach (var (name, detail) in proto.Levels)
|
||||
{
|
||||
ent.Comp.AlertLevels.Add(new($"alert-level-{name}", $"alert-level-{name}-announcement", name, !detail.DisableSelection, detail.Color));
|
||||
}
|
||||
ent.Comp.CanSetAlertAt = Timing.CurTime + TimeSpan.FromSeconds(alertLevel.CurrentDelay);
|
||||
if (alertLevel.IsLevelLocked)
|
||||
ent.Comp.CanSetAlertAt = null;
|
||||
ent.Comp.ShuttlesCallable = ShuttlesCallable();
|
||||
|
||||
ent.Comp.ExpectedEvacuationArrival = _roundEnd.ExpectedCountdownEnd;
|
||||
ent.Comp.ExpectedEvacuationDuration = _roundEnd.ExpectedShuttleLength;
|
||||
if (TryComp<StationExfiltrationComponent>(station, out var exfiltration))
|
||||
ent.Comp.ExpectedExfiltrationArrival = exfiltration.ArrivalTime;
|
||||
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private bool ShuttlesCallable()
|
||||
{
|
||||
// Defer to what the round end system thinks we should be able to do.
|
||||
if (_emergencyShuttle.EmergencyShuttleArrived || !_roundEnd.CanCallOrRecall())
|
||||
return false;
|
||||
|
||||
// Calling shuttle checks
|
||||
if (_roundEnd.ExpectedCountdownEnd is null)
|
||||
return true;
|
||||
|
||||
// Recalling shuttle checks
|
||||
var recallThreshold = _configuration.GetCVar(CCVars.EmergencyRecallTurningPoint);
|
||||
|
||||
// shouldn't really be happening if we got here
|
||||
if (_roundEnd.ShuttleTimeLeft is not { } left
|
||||
|| _roundEnd.ExpectedShuttleLength is not { } expected)
|
||||
return false;
|
||||
|
||||
return !(left.TotalSeconds / expected.TotalSeconds < recallThreshold);
|
||||
}
|
||||
|
||||
protected override void OnAlertLevel(Entity<DVCommunicationsConsoleComponent> ent, ref DVCommunicationsConsoleAlertLevelMessage args)
|
||||
{
|
||||
base.OnAlertLevel(ent, ref args);
|
||||
|
||||
if (!AccessReader.IsAllowed(ent, args.Actor))
|
||||
return;
|
||||
|
||||
if (_station.GetOwningStation(ent) is not { } station)
|
||||
return;
|
||||
|
||||
_alertLevel.SetLevel(station, args.AlertLevel, true, true);
|
||||
AdminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):player} has set the alert level to {args.AlertLevel:level} on {ToPrettyString(station):station} using {ToPrettyString(ent):console}");
|
||||
}
|
||||
|
||||
protected override void OnEvacuationShuttle(Entity<DVCommunicationsConsoleComponent> ent, ref DVCommunicationsConsoleEvacuationShuttleMessage args)
|
||||
{
|
||||
base.OnEvacuationShuttle(ent, ref args);
|
||||
|
||||
if (!AccessReader.IsAllowed(ent, args.Actor))
|
||||
return;
|
||||
|
||||
if (args.Call)
|
||||
{
|
||||
var ev = new CommunicationConsoleCallShuttleAttemptEvent(ent, default!, args.Actor);
|
||||
RaiseLocalEvent(ref ev);
|
||||
|
||||
_roundEnd.RequestRoundEnd(args.Actor);
|
||||
AdminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):player} has called the evacuation shuttle using {ToPrettyString(ent):console}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_roundEnd.CancelRoundEndCountdown(args.Actor);
|
||||
AdminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):player} has recalled the evacuation shuttle using {ToPrettyString(ent):console}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnExfiltrationShuttle(Entity<DVCommunicationsConsoleComponent> ent, ref DVCommunicationsConsoleExfiltrationShuttleMessage args)
|
||||
{
|
||||
base.OnExfiltrationShuttle(ent, ref args);
|
||||
|
||||
if (!AccessReader.IsAllowed(ent, args.Actor))
|
||||
return;
|
||||
|
||||
if (_station.GetOwningStation(ent) is not { } station)
|
||||
return;
|
||||
|
||||
if (args.Call)
|
||||
{
|
||||
_stationExfiltration.Call(station);
|
||||
AdminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):player} has called the exfiltration shuttle using {ToPrettyString(ent):console}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_stationExfiltration.Recall(station);
|
||||
AdminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):player} has recalled the exfiltration shuttle using {ToPrettyString(ent):console}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public sealed partial class GameTicker
|
||||
public bool ConvertRoundToSurvival()
|
||||
{
|
||||
// Ramping scheduler is already added. Do nothing.
|
||||
if (IsGameRuleActive(RampingSchedulerProto))
|
||||
if (IsGameRuleAdded(RampingSchedulerProto))
|
||||
{
|
||||
_chatManager.SendAdminAlert("RampingStationEventScheduler detected. No rules added.");
|
||||
return true;
|
||||
@@ -44,7 +44,7 @@ public sealed partial class GameTicker
|
||||
var rampingSchedulerStart = _gameTiming.CurTime.Add(GracePeriod);
|
||||
EnsureComp<DelayedStartRuleComponent>(rampingScheduler).RuleStartTime = rampingSchedulerStart;
|
||||
|
||||
_chatManager.SendAdminAlert($"Major antag defeated. Converting to survival at {rampingSchedulerStart}.");
|
||||
_chatManager.SendAdminAlert($"Major antag defeated. Converting to survival at {(RoundDuration() + GracePeriod).ToString(@"hh\:mm\:ss")}.");
|
||||
|
||||
// End Basic Rules
|
||||
var basicRules = EntityQueryEnumerator<BasicStationEventSchedulerComponent>();
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
using Content.Server.AlertLevel;
|
||||
using Content.Server.Audio.Jukebox;
|
||||
using Content.Server.Instruments;
|
||||
using Content.Server.Light.Components;
|
||||
using Content.Server.Light.EntitySystems;
|
||||
using Content.Server.Nuke;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Shared._DV.KeycardAuthenticationDevice;
|
||||
using Content.Shared.Audio.Jukebox;
|
||||
using Content.Shared.Light.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._DV.KeycardAuthenticationDevice;
|
||||
|
||||
public sealed class DVStationKeycardAuthenticationDeviceSystem : SharedDVStationKeycardAuthenticationDeviceSystem
|
||||
{
|
||||
[Dependency] private readonly NukeCodePaperSystem _nukeCodePaper = default!;
|
||||
[Dependency] private readonly AlertLevelSystem _alertLevel = default!;
|
||||
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly PoweredLightSystem _poweredLight = default!;
|
||||
[Dependency] private readonly PointLightSystem _pointLight = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _userInterface = default!;
|
||||
[Dependency] private readonly JukeboxSystem _jukebox = default!;
|
||||
|
||||
protected override void Mayday(Entity<DVStationKeycardAuthenticationDeviceComponent> station)
|
||||
{
|
||||
base.Mayday(station);
|
||||
|
||||
_alertLevel.SetLevel(station, "zeta", true, true, true, true);
|
||||
var alertLevel = Comp<AlertLevelComponent>(station);
|
||||
var level = _prototype.Index<AlertLevelPrototype>(alertLevel.AlertLevelPrototype).Levels[alertLevel.CurrentLevel];
|
||||
_roundEnd.RequestRoundEnd(level.ShuttleTime, null, null, false, cantRecall: true);
|
||||
|
||||
var bulbQuery = GetEntityQuery<LightBulbComponent>();
|
||||
var tubeQuery = EntityQueryEnumerator<PoweredLightComponent>();
|
||||
while (tubeQuery.MoveNext(out var uid, out var light))
|
||||
{
|
||||
if (Station.GetOwningStation(uid) != station.Owner)
|
||||
continue;
|
||||
|
||||
if (_poweredLight.GetBulb(uid, light) is not { } bulb || !bulbQuery.TryComp(bulb, out var bulbComp))
|
||||
continue;
|
||||
|
||||
bulbComp.LightEnergy /= 2;
|
||||
bulbComp.PowerUse /= 2;
|
||||
|
||||
_poweredLight.SetState(uid, light.On, light);
|
||||
}
|
||||
|
||||
var emergencyQuery = EntityQueryEnumerator<EmergencyLightComponent, PointLightComponent>();
|
||||
while (emergencyQuery.MoveNext(out var uid, out var emergency, out var light))
|
||||
{
|
||||
if (Station.GetOwningStation(uid) != station.Owner)
|
||||
continue;
|
||||
|
||||
_pointLight.SetEnergy(uid, emergency.MaydayEnergy, light);
|
||||
_pointLight.SetRadius(uid, emergency.MaydayRadius, light);
|
||||
_pointLight.SetFalloff(uid, emergency.MaydayFalloff, light);
|
||||
}
|
||||
|
||||
var instrumentQuery = EntityQueryEnumerator<InstrumentComponent>();
|
||||
while (instrumentQuery.MoveNext(out var uid, out _))
|
||||
{
|
||||
if (Station.GetOwningStation(uid) != station.Owner)
|
||||
continue;
|
||||
|
||||
_userInterface.CloseUis(uid);
|
||||
}
|
||||
|
||||
var jukeboxQuery = EntityQueryEnumerator<JukeboxComponent>();
|
||||
while (jukeboxQuery.MoveNext(out var uid, out var jukebox))
|
||||
{
|
||||
if (Station.GetOwningStation(uid) != station.Owner)
|
||||
continue;
|
||||
|
||||
_jukebox.Stop((uid, jukebox));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Scuttling(Entity<DVStationKeycardAuthenticationDeviceComponent> station)
|
||||
{
|
||||
base.Scuttling(station);
|
||||
|
||||
_nukeCodePaper.SendNukeCodes(station);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using Content.Server.NodeContainer.Nodes;
|
||||
using Content.Shared._DV.NodeCrawl;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.NodeContainer;
|
||||
using Content.Shared.Polymorph;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -30,6 +31,7 @@ public sealed class NodeCrawlSystem : SharedNodeCrawlSystem
|
||||
SubscribeLocalEvent<NodeCrawlerComponent, InhaleLocationEvent>(OnInhaleLocation);
|
||||
SubscribeLocalEvent<NodeCrawlerComponent, ExhaleLocationEvent>(OnExhaleLocation);
|
||||
SubscribeLocalEvent<NodeCrawlerComponent, AtmosExposedGetAirEvent>(OnGetAir);
|
||||
SubscribeLocalEvent<NodeCrawlerComponent, PolymorphActionEvent>(OnPolymorph);
|
||||
|
||||
SubscribeLocalEvent<NodeCrawlerComponent, EntityZombifiedEvent>(OnZombify);
|
||||
}
|
||||
@@ -222,4 +224,15 @@ public sealed class NodeCrawlSystem : SharedNodeCrawlSystem
|
||||
ent.Comp.EnterDelay = ent.Comp.ZombieEnterDelay;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exit on polymorph or else the entity's movement bugs out.
|
||||
/// </summary>
|
||||
/// <param name="ent"></param>
|
||||
/// <param name="args"></param>
|
||||
private void OnPolymorph(Entity<NodeCrawlerComponent> ent, ref PolymorphActionEvent args)
|
||||
{
|
||||
if (ent.Comp.Mover.HasValue)
|
||||
ExitNodeCrawl(ent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
using Content.Server.AlertLevel;
|
||||
using Content.Server.Screens.Components;
|
||||
using Content.Shared._DV.Communications;
|
||||
using Content.Shared._DV.Screens;
|
||||
using Content.Shared.DeviceNetwork.Events;
|
||||
using Content.Shared.Station;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server._DV.Screens;
|
||||
|
||||
public sealed class DVScreenSystem : DVSharedScreenSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedStationSystem _station = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DVScreenComponent, DeviceNetworkPacketEvent>(OnPacket);
|
||||
SubscribeLocalEvent<AlertLevelChangedEvent>(OnAlertLevelChanged);
|
||||
}
|
||||
|
||||
private void OnPacket(Entity<DVScreenComponent> ent, ref DeviceNetworkPacketEvent args)
|
||||
{
|
||||
if (args.Data.TryGetValue(ShuttleTimerMasks.ShuttleMap, out _))
|
||||
OnShuttlePacket(ent, ref args);
|
||||
if (args.Data.TryGetValue(DVScreenPackets.Text, out (string, string)? text))
|
||||
OnTextPacket(ent, text.Value, ref args);
|
||||
if (args.Data.TryGetValue(DVScreenPackets.ShowBorders, out bool? showBorders))
|
||||
OnBordersPacket(ent, showBorders.Value, ref args);
|
||||
if (args.Data.TryGetValue(DVScreenPackets.Content, out DVScreenContent? content))
|
||||
OnContentPacket(ent, content.Value, ref args);
|
||||
}
|
||||
|
||||
private void OnShuttlePacket(Entity<DVScreenComponent> ent, ref DeviceNetworkPacketEvent args)
|
||||
{
|
||||
var xform = Transform(ent);
|
||||
|
||||
args.Data.TryGetValue(ShuttleTimerMasks.ShuttleMap, out EntityUid? shuttleMap);
|
||||
args.Data.TryGetValue(ShuttleTimerMasks.SourceMap, out EntityUid? source);
|
||||
args.Data.TryGetValue(ShuttleTimerMasks.DestMap, out EntityUid? dest);
|
||||
args.Data.TryGetValue(ShuttleTimerMasks.Docked, out bool docked);
|
||||
var screenIsAtDestination = docked;
|
||||
string key;
|
||||
|
||||
switch (xform.MapUid)
|
||||
{
|
||||
// sometimes the timer transforms on FTL shuttles have a hyperspace mapuid, so matching by grid works as a fallback.
|
||||
case var local when local == shuttleMap || xform.GridUid == shuttleMap:
|
||||
key = ShuttleTimerMasks.ShuttleTime;
|
||||
break;
|
||||
case var origin when origin == source:
|
||||
key = ShuttleTimerMasks.SourceTime;
|
||||
break;
|
||||
case var remote when remote == dest:
|
||||
key = ShuttleTimerMasks.DestTime;
|
||||
screenIsAtDestination = false;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.Data.TryGetValue(key, out TimeSpan duration))
|
||||
return;
|
||||
|
||||
ent.Comp.ScreenIsAtDestination = screenIsAtDestination;
|
||||
ent.Comp.TargetTime = _timing.CurTime + duration;
|
||||
Dirty(ent);
|
||||
UpdateVisuals(ent);
|
||||
}
|
||||
|
||||
private void OnTextPacket(Entity<DVScreenComponent> ent, (string, string) text, ref DeviceNetworkPacketEvent args)
|
||||
{
|
||||
ent.Comp.Line1 = text.Item1;
|
||||
ent.Comp.Line2 = text.Item2;
|
||||
|
||||
Dirty(ent);
|
||||
UpdateVisuals(ent);
|
||||
}
|
||||
|
||||
private void OnBordersPacket(Entity<DVScreenComponent> ent, bool showBorders, ref DeviceNetworkPacketEvent args)
|
||||
{
|
||||
ent.Comp.ShowAlertBorder = showBorders;
|
||||
|
||||
Dirty(ent);
|
||||
UpdateVisuals(ent);
|
||||
}
|
||||
|
||||
private void OnContentPacket(Entity<DVScreenComponent> ent, DVScreenContent content, ref DeviceNetworkPacketEvent args)
|
||||
{
|
||||
ent.Comp.Content = content;
|
||||
|
||||
Dirty(ent);
|
||||
UpdateVisuals(ent);
|
||||
}
|
||||
|
||||
private void OnAlertLevelChanged(AlertLevelChangedEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<DVScreenComponent>();
|
||||
while (query.MoveNext(out var uid, out var screen))
|
||||
{
|
||||
if (_station.GetOwningStation(uid) != ev.Station)
|
||||
continue;
|
||||
|
||||
screen.AlertLevel = ev.AlertLevel;
|
||||
Dirty(uid, screen);
|
||||
UpdateVisuals((uid, screen));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,6 @@ public sealed class StationExfiltrationSystem : EntitySystem
|
||||
[Dependency] private readonly ShuttleSystem _shuttle = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly NavMapSystem _navMap = default!;
|
||||
[Dependency] private readonly CommunicationsConsoleSystem _communicationsConsole = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -169,7 +168,8 @@ public sealed class StationExfiltrationSystem : EntitySystem
|
||||
_chat.DispatchStationAnnouncement(ent, Loc.GetString(ent.Comp.CalledAnnouncement, ("time", ent.Comp.TravelTime.TotalSeconds), ("station", Name(ent))), sender: Loc.GetString(ent.Comp.Sender), colorOverride: Color.Gold);
|
||||
}
|
||||
|
||||
_communicationsConsole.UpdateCommsConsoleInterface();
|
||||
var evt = new StationExfiltrationChangedEvent((ent, ent.Comp), true);
|
||||
RaiseLocalEvent(ref evt);
|
||||
}
|
||||
|
||||
public void Recall(Entity<StationExfiltrationComponent?> ent)
|
||||
@@ -180,6 +180,10 @@ public sealed class StationExfiltrationSystem : EntitySystem
|
||||
ent.Comp.ArrivalTime = null;
|
||||
_chat.DispatchStationAnnouncement(ent, Loc.GetString(ent.Comp.RecalledAnnouncement, ("station", Name(ent))), sender: Loc.GetString(ent.Comp.Sender), colorOverride: Color.Gold);
|
||||
|
||||
_communicationsConsole.UpdateCommsConsoleInterface();
|
||||
var evt = new StationExfiltrationChangedEvent((ent, ent.Comp), false);
|
||||
RaiseLocalEvent(ref evt);
|
||||
}
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public readonly record struct StationExfiltrationChangedEvent(Entity<StationExfiltrationComponent> Station, bool Exfiltrating);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Server._DV.StationEvents.Components;
|
||||
using Content.Server.Antag;
|
||||
using Content.Server.Communications;
|
||||
using Content.Server.StationEvents.Events;
|
||||
using Content.Shared._DV.Communications;
|
||||
using Content.Shared.Forensics.Components;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Ghost;
|
||||
@@ -43,7 +43,7 @@ public sealed class FugitiveRule : StationEventSystem<FugitiveRuleComponent>
|
||||
ChatSystem.DispatchGlobalAnnouncement(announcement, sender: sender, colorOverride: comp.Color);
|
||||
|
||||
// send the report to every comms console on the station
|
||||
var query = EntityQueryEnumerator<TransformComponent, CommunicationsConsoleComponent>();
|
||||
var query = EntityQueryEnumerator<TransformComponent, DVCommunicationsConsoleComponent>();
|
||||
var consoles = new List<TransformComponent>();
|
||||
while (query.MoveNext(out var console, out var xform, out _))
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ using Content.Shared._Goobstation.Flashbang;
|
||||
using Content.Shared._Starlight.Flash.Components;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Flash; // Delta V - Flash Work
|
||||
using Content.Shared._DV.Body.Components; // Delta V - Remove various comps related to breathing
|
||||
|
||||
|
||||
namespace Content.Server._Starlight;
|
||||
@@ -68,6 +69,8 @@ public sealed class ShadekinSystem : EntitySystem
|
||||
{
|
||||
UpdateAlert(uid, component, (short)component.CurrentState);
|
||||
RemComp<InternalsComponent>(uid);
|
||||
RemComp<RespiratorComponent>(uid);
|
||||
RemComp<AffectedByCPRComponent>(uid); // No lungs = no CPR
|
||||
}
|
||||
|
||||
private void OnEyeColorChange(EntityUid uid, ShadekinComponent component, EyeColorInitEvent args)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared._DV.Humanoid;
|
||||
using Content.Shared._DV.Humanoid; // DeltaV
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Sprite;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -18,7 +16,6 @@ public abstract partial class SharedVisualBodySystem : EntitySystem
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly MarkingManager _marking = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedScaleVisualsSystem _scaleVisualsSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -28,7 +25,6 @@ public abstract partial class SharedVisualBodySystem : EntitySystem
|
||||
SubscribeLocalEvent<VisualOrganMarkingsComponent, BodyRelayedEvent<OrganCopyAppearanceEvent>>(OnMarkingsOrganCopyAppearance);
|
||||
SubscribeLocalEvent<VisualOrganComponent, BodyRelayedEvent<ApplyOrganProfileDataEvent>>(OnVisualOrganApplyProfile);
|
||||
SubscribeLocalEvent<VisualOrganMarkingsComponent, BodyRelayedEvent<ApplyOrganMarkingsEvent>>(OnMarkingsOrganApplyMarkings);
|
||||
SubscribeLocalEvent<HumanoidProfileComponent, ApplyOrganProfileDataEvent>(OnApplyOrganProfileData); // Delta V - Taking the solution from CD
|
||||
|
||||
InitializeModifiers();
|
||||
InitializeInitial();
|
||||
@@ -100,36 +96,9 @@ public abstract partial class SharedVisualBodySystem : EntitySystem
|
||||
if (!other.Layer.Equals(ent.Comp.Layer))
|
||||
return;
|
||||
|
||||
// Delta V - Begin Fix Height for Cloning
|
||||
var height = other.Profile.Height;
|
||||
if (TryComp<HumanoidProfileComponent>(args.Body.Owner, out var component))
|
||||
ScaleBody((args.Body.Owner, component), height, height);
|
||||
// Delta V - End
|
||||
|
||||
SetOrganAppearance(ent, other.Data);
|
||||
}
|
||||
|
||||
// Delta V - BEGIN CD Solution
|
||||
private void OnApplyOrganProfileData(Entity<HumanoidProfileComponent> entity, ref ApplyOrganProfileDataEvent args)
|
||||
{
|
||||
var speciesPrototype = _prototype.Index(entity.Comp.Species);
|
||||
if (args.Base == null)
|
||||
return;
|
||||
|
||||
var height = Math.Clamp(MathF.Round(args.Base.Value.Height, 2), speciesPrototype.MinHeight, speciesPrototype.MaxHeight);
|
||||
|
||||
ScaleBody(entity, speciesPrototype.ScaleHeight ? height : 1f, height);
|
||||
}
|
||||
|
||||
private void ScaleBody(Entity<HumanoidProfileComponent> entity, float heightX, float heightY)
|
||||
{
|
||||
_scaleVisualsSystem.SetSpriteScale(
|
||||
entity.Owner,
|
||||
new Vector2(heightX, heightY)
|
||||
);
|
||||
}
|
||||
// Delta V - END
|
||||
|
||||
|
||||
private void OnMarkingsOrganCopyAppearance(Entity<VisualOrganMarkingsComponent> ent, ref BodyRelayedEvent<OrganCopyAppearanceEvent> args)
|
||||
{
|
||||
|
||||
@@ -18,9 +18,8 @@ namespace Content.Shared.Communications
|
||||
public List<string>? AlertLevels;
|
||||
public string CurrentAlert;
|
||||
public float CurrentAlertDelay;
|
||||
public readonly TimeSpan? ExpectedExfiltrationCountdownEnd;
|
||||
|
||||
public CommunicationsConsoleInterfaceState(bool canAnnounce, bool canCall, List<string>? alertLevels, string currentAlert, float currentAlertDelay, TimeSpan? expectedCountdownEnd, TimeSpan? expectedExfiltrationCountdownEnd) // DeltaV - Exfiltration Shuttle
|
||||
public CommunicationsConsoleInterfaceState(bool canAnnounce, bool canCall, List<string>? alertLevels, string currentAlert, float currentAlertDelay, TimeSpan? expectedCountdownEnd)
|
||||
{
|
||||
CanAnnounce = canAnnounce;
|
||||
CanCall = canCall;
|
||||
@@ -29,7 +28,6 @@ namespace Content.Shared.Communications
|
||||
AlertLevels = alertLevels;
|
||||
CurrentAlert = currentAlert;
|
||||
CurrentAlertDelay = currentAlertDelay;
|
||||
ExpectedExfiltrationCountdownEnd = expectedExfiltrationCountdownEnd; // DeltaV - Exfiltration Shuttle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared._DV.Administration.EntitySystems; // DeltaV - Unorbitable
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Follower.Components;
|
||||
@@ -33,6 +34,7 @@ public sealed class FollowerSystem : EntitySystem
|
||||
[Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!;
|
||||
[Dependency] private readonly INetManager _netMan = default!;
|
||||
[Dependency] private readonly ISharedAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly UnorbitableSystem _unorbitable = default!; // DeltaV - Unorbitable
|
||||
|
||||
private static readonly ProtoId<TagPrototype> ForceableFollowTag = "ForceableFollow";
|
||||
private static readonly ProtoId<TagPrototype> PreventGhostnadoWarpTag = "NotGhostnadoWarpable";
|
||||
@@ -98,6 +100,11 @@ public sealed class FollowerSystem : EntitySystem
|
||||
if (ev.User == ev.Target || IsClientSide(ev.Target))
|
||||
return;
|
||||
|
||||
// DeltaV - Unorbitable START
|
||||
if (!_unorbitable.CanFollow(ev.User, ev.Target))
|
||||
return;
|
||||
// DeltaV END
|
||||
|
||||
if (HasComp<GhostComponent>(ev.User))
|
||||
{
|
||||
var verb = new AlternativeVerb()
|
||||
@@ -187,6 +194,11 @@ public sealed class FollowerSystem : EntitySystem
|
||||
if (follower == entity || TerminatingOrDeleted(entity))
|
||||
return;
|
||||
|
||||
// DeltaV - Unorbitable START
|
||||
if (!_unorbitable.CanFollow(follower, entity))
|
||||
return;
|
||||
// DeltaV END
|
||||
|
||||
// No recursion for you
|
||||
var targetXform = Transform(entity);
|
||||
while (targetXform.ParentUid.IsValid())
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Numerics; // DeltaV
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Sprite; // DeltaV
|
||||
using Robust.Shared.GameObjects.Components.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -11,6 +13,7 @@ public sealed class HumanoidProfileSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly GrammarSystem _grammar = default!;
|
||||
[Dependency] private readonly SharedScaleVisualsSystem _scale = default!; // DeltaV
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -28,6 +31,7 @@ public sealed class HumanoidProfileSystem : EntitySystem
|
||||
ent.Comp.Age = profile.Age;
|
||||
ent.Comp.Species = profile.Species;
|
||||
ent.Comp.Sex = profile.Sex;
|
||||
ent.Comp.Height = profile.Height; // DeltaV
|
||||
Dirty(ent);
|
||||
|
||||
var sexChanged = new SexChangedEvent(ent.Comp.Sex, profile.Sex);
|
||||
@@ -37,6 +41,15 @@ public sealed class HumanoidProfileSystem : EntitySystem
|
||||
{
|
||||
_grammar.SetGender((ent, grammar), profile.Gender);
|
||||
}
|
||||
|
||||
// START DeltaV - Apply profile/species size
|
||||
// Assume 1.0 scale unless the original scale exists (blame Allulalo)
|
||||
var scale = Vector2.One;
|
||||
if (TryComp<ScaleVisualsComponent>(ent, out var scaledVisuals) && scaledVisuals.OriginalScale is { } originalScale)
|
||||
scale = originalScale;
|
||||
|
||||
_scale.SetSpriteScale(ent, scale);
|
||||
// END DeltaV
|
||||
}
|
||||
|
||||
private void OnExamined(Entity<HumanoidProfileComponent> ent, ref ExaminedEvent args)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics; // DeltaV
|
||||
using Content.Shared.Body;
|
||||
using Content.Shared.Dataset;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
@@ -111,7 +112,7 @@ public sealed partial class SpeciesPrototype : IPrototype
|
||||
/// The base height scale for this species
|
||||
/// </summary>
|
||||
[DataField("baseScale")]
|
||||
public System.Numerics.Vector2 BaseScale = new(1f, 1f);
|
||||
public Vector2 BaseScale = new(1f, 1f);
|
||||
// End DV - CD Character Records shouldn't nuke species heights
|
||||
|
||||
// Begin CD - Character Records
|
||||
@@ -119,13 +120,13 @@ public sealed partial class SpeciesPrototype : IPrototype
|
||||
/// The minimum height for this species
|
||||
/// </summary>
|
||||
[DataField("minHeight")]
|
||||
public float MinHeight = 0.9f; // DeltaV - less trolling with the heights
|
||||
public float MinHeight = 0.8f; // DeltaV
|
||||
|
||||
/// <summary>
|
||||
/// The maximum height for this species
|
||||
/// </summary>
|
||||
[DataField("maxHeight")]
|
||||
public float MaxHeight = 1.1f; // DeltaV - less trolling with the heights
|
||||
public float MaxHeight = 1.2f; // DeltaV
|
||||
|
||||
/// <summary>
|
||||
/// The default height for this species
|
||||
|
||||
@@ -90,7 +90,7 @@ public sealed partial class LabelSystem : EntitySystem
|
||||
private void OnRefreshNameModifiers(Entity<LabelComponent> entity, ref RefreshNameModifiersEvent args)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(entity.Comp.CurrentLabel))
|
||||
args.AddModifier("comp-label-format", extraArgs: ("label", entity.Comp.CurrentLabel));
|
||||
args.AddModifier("comp-label-format", 100, extraArgs: ("label", entity.Comp.CurrentLabel)); // Coyote: add priority of 100 to reverse order of label and baseName
|
||||
}
|
||||
|
||||
private void OnComponentInit(Entity<PaperLabelComponent> ent, ref ComponentInit args)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Shared._DV.Body.Components; // DeltaV
|
||||
using Content.Shared._DV.Body.Systems; // DeltaV
|
||||
using Content.Shared._ST.Interaction; // Stellar - interaction particles
|
||||
using Content.Shared._Floof.OfferItem; // Floof
|
||||
using Content.Shared.ActionBlocker;
|
||||
@@ -10,6 +12,7 @@ using Content.Shared.Database;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Humanoid; // DeltaV
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Interaction;
|
||||
@@ -55,6 +58,7 @@ public sealed class PullingSystem : EntitySystem
|
||||
[Dependency] private readonly HeldSpeedModifierSystem _clothingMoveSpeed = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedVirtualItemSystem _virtual = default!;
|
||||
[Dependency] private readonly SmallCharacterSystem _smallCharacter = default!; // DeltaV
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -295,6 +299,14 @@ public sealed class PullingSystem : EntitySystem
|
||||
|
||||
private void OnRefreshMovespeed(EntityUid uid, PullerComponent component, RefreshMovementSpeedModifiersEvent args)
|
||||
{
|
||||
// BEGIN DeltaV - Slow if smaller puller
|
||||
if (TryComp<SmallCharacterComponent>(uid, out var smol))
|
||||
{
|
||||
var sizePenalty = _smallCharacter.ApplyPullSpeedPenalty((uid, smol), component.Pulling);
|
||||
args.ModifySpeed(sizePenalty, sizePenalty);
|
||||
}
|
||||
// END DeltaV
|
||||
|
||||
if (TryComp<HeldSpeedModifierComponent>(component.Pulling, out var heldMoveSpeed) && component.Pulling.HasValue)
|
||||
{
|
||||
var (walkMod, sprintMod) =
|
||||
|
||||
@@ -97,7 +97,7 @@ public sealed partial class ParcelWrappingSystem
|
||||
if (target == user)
|
||||
{
|
||||
var selfMsg = Loc.GetString("parcel-wrap-popup-being-wrapped-self");
|
||||
_popup.PopupClient(selfMsg, user, user);
|
||||
_popup.PopupEntity(selfMsg, user, user);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -143,7 +143,7 @@ public sealed partial class ParcelWrappingSystem
|
||||
|
||||
// Spawn the actual parcel entity.
|
||||
var targetTransform = Transform(target);
|
||||
var spawned = Spawn(GetParcelPrototype(wrapper, target), targetTransform.Coordinates);
|
||||
var spawned = SpawnAtPosition(GetParcelPrototype(wrapper, target), targetTransform.Coordinates);
|
||||
_transform.SetLocalRotation(spawned, targetTransform.LocalRotation);
|
||||
|
||||
// If the target is in a container, try to put the parcel in its place in the container.
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace Content.Shared.Preferences
|
||||
_traitPreferences = traitPreferences;
|
||||
_loadouts = loadouts;
|
||||
// Begin CD - Character Records
|
||||
Height = height;
|
||||
Height = height; // This is the user-set scale on the profile editor. Not actual height measurements.
|
||||
CDCharacterRecords = cdCharacterRecords;
|
||||
// End CD - Character Records
|
||||
|
||||
|
||||
@@ -300,4 +300,12 @@ public sealed partial class RevenantComponent : Component
|
||||
public string HarvestingState = "harvesting";
|
||||
#endregion
|
||||
[DataField] public EntityUid? HauntAction; // Imp
|
||||
|
||||
#region Melee
|
||||
/// <summary>
|
||||
/// DeltaV - The amount of time a rev is revealed after making a melee attack.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan MeleeRevealTime = TimeSpan.FromSeconds(2);
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -25,11 +25,27 @@ public sealed partial class ScaleVisualsComponent : Component
|
||||
[ViewVariables]
|
||||
public Vector2? OriginalScale;
|
||||
|
||||
// Delta V Addition
|
||||
/// <summary>
|
||||
/// Base Scale of the Species, which we use to set a new height relative to this.
|
||||
/// DeltaV - Contains the species scale. Set dynamically by
|
||||
/// baseScale in the Species prototype.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
[ViewVariables]
|
||||
public Vector2 SpeciesScale = Vector2.One;
|
||||
public Vector2 SpeciesScale = new(1f, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Contains the user-defined scale from the character creation
|
||||
/// screen.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
[ViewVariables]
|
||||
public Vector2 ProfileScale = new(1f, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Contains the computer scale from applying Scale, SpeciesScale, and ProfileScale.
|
||||
/// This will contain the actual scale after all modifiers are applied.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
[ViewVariables]
|
||||
public Vector2 ComputedScale;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Humanoid; // DeltaV
|
||||
using Robust.Shared.Prototypes; // DeltaV
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Sprite;
|
||||
@@ -6,6 +8,7 @@ namespace Content.Shared.Sprite;
|
||||
public abstract class SharedScaleVisualsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!; // DeltaV
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -39,15 +42,28 @@ public abstract class SharedScaleVisualsSystem : EntitySystem
|
||||
{
|
||||
var comp = EnsureComp<ScaleVisualsComponent>(uid);
|
||||
comp.Scale = scale;
|
||||
|
||||
// BEGIN DeltaV - Apply species and profile height
|
||||
// We have to ensure this is idempotent so that if it gets applied more than once
|
||||
// the sprite size at the end is the same.
|
||||
if (TryComp<HumanoidProfileComponent>(uid, out var profile))
|
||||
{
|
||||
var speciesProto = _proto.Index(profile.Species);
|
||||
comp.SpeciesScale = speciesProto.BaseScale;
|
||||
scale *= comp.SpeciesScale; // Apply both species scale and character-defined height
|
||||
|
||||
Vector2 profileHeight = new(profile.Height, profile.Height);
|
||||
comp.ProfileScale = profileHeight;
|
||||
scale *= comp.ProfileScale;
|
||||
}
|
||||
|
||||
comp.ComputedScale = scale; // Nice to know what the computed size is in case of bugs
|
||||
// END DeltaV
|
||||
|
||||
Dirty(uid, comp);
|
||||
|
||||
// Delta V - Begin Species Scaling
|
||||
// 120% species scale => add 0.2 to scale
|
||||
var newScale = scale + comp.SpeciesScale - Vector2.One;
|
||||
// Delta V - End Species Scaling
|
||||
|
||||
var appearanceComponent = EnsureComp<AppearanceComponent>(uid);
|
||||
_appearance.SetData(uid, ScaleVisuals.Scale, newScale /* Delta V - Custom Species Scale */, appearanceComponent);
|
||||
_appearance.SetData(uid, ScaleVisuals.Scale, scale, appearanceComponent);
|
||||
|
||||
// Raise an event for content use.
|
||||
var ev = new ScaleEntityEvent(uid, scale);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Administration.Components;
|
||||
|
||||
/// This component prevents an entity from being followed by ghosts.
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class UnorbitableComponent : Component
|
||||
{
|
||||
/// Whether to allow adminned players to follow the entity.
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool AllowAdmins = false;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Content.Shared._DV.Administration.Components;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Follower;
|
||||
using Content.Shared.Follower.Components;
|
||||
|
||||
namespace Content.Shared._DV.Administration.EntitySystems;
|
||||
|
||||
public sealed class UnorbitableSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly FollowerSystem _followerSystem = default!;
|
||||
[Dependency] private readonly ISharedAdminManager _admin = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<UnorbitableComponent, ComponentInit>(OnInit);
|
||||
}
|
||||
|
||||
private void OnInit(Entity<UnorbitableComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
if (!HasComp<FollowedComponent>(ent))
|
||||
return;
|
||||
|
||||
_followerSystem.StopAllFollowers(ent);
|
||||
}
|
||||
|
||||
public bool CanFollow(EntityUid follower, EntityUid target)
|
||||
{
|
||||
if (TryComp<UnorbitableComponent>(target, out var unorbitable))
|
||||
{
|
||||
return unorbitable.AllowAdmins && _admin.IsAdmin(follower);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Shared._DV.Body.Systems;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Body.Components;
|
||||
|
||||
/// <summary>
|
||||
/// If an entity has this, if a small character has penalties (such as pull speed),
|
||||
/// the small character will ignore the penalties associated with their size.
|
||||
///
|
||||
/// Mostly used for things like wheeled/floating objects.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[AutoGenerateComponentState]
|
||||
[Access(typeof(SmallCharacterSystem))]
|
||||
public sealed partial class SmallCharacterComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The speed of which to scale the small character's pull speed by if the
|
||||
/// object is big enough to warrant a pull-speed slowdown.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float PullSpeedPenalty = 1f;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Content.Shared._DV.Body.Components;
|
||||
|
||||
/// <summary>
|
||||
/// If an entity has this, if a small character has penalties (such as pull speed),
|
||||
/// the small character will ignore the penalties associated with their size.
|
||||
///
|
||||
/// Mostly used for things like wheeled/floating objects.
|
||||
///
|
||||
/// See <see cref="Systems.SmallCharacterSystem"/>
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class UnaffectedBySizePenaltyComponent : Component;
|
||||
@@ -0,0 +1,97 @@
|
||||
using Content.Shared._DV.Body.Components;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Item;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Physics.Components;
|
||||
|
||||
namespace Content.Shared._DV.Body.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Used to relay or subscribe to events if a character's scale is 1.0 or below.
|
||||
/// This is only used for the height slider scale.
|
||||
/// </summary>
|
||||
public sealed partial class SmallCharacterSystem : EntitySystem
|
||||
{
|
||||
private const float NO_PENALTY = 1.0f;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnSpawn);
|
||||
}
|
||||
|
||||
private void OnSpawn(PlayerSpawnCompleteEvent ev)
|
||||
{
|
||||
if (TryComp<HumanoidProfileComponent>(ev.Mob, out var profile))
|
||||
ApplySmallCharacter(ev.Mob, profile.Height);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public float ApplyPullSpeedPenalty(Entity<SmallCharacterComponent?> puller, EntityUid? pulledEntity)
|
||||
{
|
||||
// Ignore if they aren't pulling anything...
|
||||
if (!pulledEntity.HasValue)
|
||||
return NO_PENALTY;
|
||||
|
||||
// Ignore if they aren't a small character in the first place
|
||||
if (!Resolve(puller, ref puller.Comp, false))
|
||||
return NO_PENALTY;
|
||||
|
||||
// If the pulled entity has the component that ignores the penalty
|
||||
if (HasComp<UnaffectedBySizePenaltyComponent>(pulledEntity))
|
||||
return NO_PENALTY;
|
||||
|
||||
// Ignore if it's an item that can be held or stored. It would be weird to
|
||||
// slow by X% from pulling a piece of paper or a gun when you can just hold it
|
||||
// and not suffer from a penalty.
|
||||
if (HasComp<ItemComponent>(pulledEntity))
|
||||
return NO_PENALTY;
|
||||
|
||||
// Ignore if the object is floating in the air.
|
||||
if (TryComp<PhysicsComponent>(pulledEntity, out var pulledPhysics)
|
||||
&& pulledPhysics.BodyStatus == BodyStatus.InAir)
|
||||
return NO_PENALTY;
|
||||
|
||||
return puller.Comp.PullSpeedPenalty;
|
||||
}
|
||||
|
||||
#region Static Members
|
||||
/// <summary>
|
||||
/// Gets the move-speed penalty as a float. Should be applied multiplicatively.
|
||||
/// Caps at 1 so we don't make bigger characters faster when pulling.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static float GetPullSpeedPenaltyFromScale(float scale = 1.0f)
|
||||
{
|
||||
return Math.Min(scale * scale, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a well-formed display string of the pull speed penalty.
|
||||
/// Used primarily in the character editor to get the well-formed percent
|
||||
/// without having to duplicate formulas.
|
||||
/// </summary>
|
||||
/// <param name="scale"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public static string GetPullSpeedPenaltyDisplayFromScale(float scale = 1.0f)
|
||||
{
|
||||
return $"{Math.Round((1 - GetPullSpeedPenaltyFromScale(scale)) * 100)}%";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Members
|
||||
private void ApplySmallCharacter(EntityUid uid, float scale = 1)
|
||||
{
|
||||
if (scale >= 1)
|
||||
return;
|
||||
|
||||
// The character scale is stored in the HumanoidProfileComponent if you ever
|
||||
// need it.
|
||||
var comp = EnsureComp<SmallCharacterComponent>(uid);
|
||||
comp.PullSpeedPenalty = GetPullSpeedPenaltyFromScale(scale);
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Communications;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CommunicationsConsoleExfiltrationShuttleMessage(bool call) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly bool Call = call;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
using Content.Shared._DV.KeycardAuthenticationDevice;
|
||||
using Content.Shared._DV.Screens;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Communications;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||
[Access(typeof(SharedDVCommunicationsConsoleSystem))]
|
||||
public sealed partial class DVCommunicationsConsoleComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public LocId AnnouncementTitle = "comms-console-announcement-title-station";
|
||||
|
||||
[DataField]
|
||||
public Color AnnouncementColor = Color.Gold;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier AnnouncementSound = new SoundPathSpecifier("/Audio/Announcements/announce.ogg");
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool GlobalAnnouncements;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool CanAnnounce = true;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool CanAlertLevel = true;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool CanCallShuttles = true;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool CanConfigureScreens = true;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool CanKeycardAuthenticationDevice = true;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public TimeSpan CanAnnounceAt = TimeSpan.Zero;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan AnnouncementInterval = TimeSpan.FromSeconds(90f);
|
||||
|
||||
[DataField]
|
||||
public TimeSpan InitialAnnouncementDelay = TimeSpan.FromSeconds(30f);
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public string CurrentAlertLevel = string.Empty;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<DVCommunicationsConsoleAlertLevel> AlertLevels = new();
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public TimeSpan? CanSetAlertAt = TimeSpan.Zero;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool ShuttlesCallable = true;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public TimeSpan? ExpectedEvacuationArrival;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public TimeSpan? ExpectedEvacuationDuration;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public TimeSpan? ExpectedExfiltrationArrival;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public DVScreenContent LastConfiguredContent = DVScreenContent.Text;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool LastConfiguredShowBorders;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public string LastConfiguredLine1 = string.Empty;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public string LastConfiguredLine2 = string.Empty;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public readonly record struct DVCommunicationsConsoleAlertLevel(LocId AlertLevel, LocId Description, string Id, bool CanSet, Color Color);
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DVCommunicationsConsoleEvacuationShuttleMessage(bool call) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly bool Call = call;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DVCommunicationsConsoleExfiltrationShuttleMessage(bool call) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly bool Call = call;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DVCommunicationsConsoleKeycardAuthenticationDeviceMessage(DVStationKeycardAction action) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly DVStationKeycardAction Action = action;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DVCommunicationsConsoleAnnouncementMessage(string announcement) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly string Announcement = announcement;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DVCommunicationsConsoleAlertLevelMessage(string alertLevel) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly string AlertLevel = alertLevel;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DVCommunicationsConsoleScreenConfigurationMessage(DVScreenContent content, bool showBorder, string line1, string line2) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly DVScreenContent Content = content;
|
||||
public readonly bool ShowBorder = showBorder;
|
||||
public readonly string Line1 = line1;
|
||||
public readonly string Line2 = line2;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DVCommunicationsConsoleUi : byte
|
||||
{
|
||||
Key,
|
||||
}
|
||||
|
||||
public static class DVScreenPackets
|
||||
{
|
||||
public const string Content = "dv-screen-content";
|
||||
public const string ShowBorders = "dv-screen-borders";
|
||||
public const string Text = "dv-screen-text";
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
using Content.Shared._DV.KeycardAuthenticationDevice;
|
||||
using Content.Shared._DV.Screens;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DeviceNetwork;
|
||||
using Content.Shared.DeviceNetwork.Events;
|
||||
using Content.Shared.DeviceNetwork.Systems;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._DV.Communications;
|
||||
|
||||
public abstract class SharedDVCommunicationsConsoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedDeviceNetworkSystem _deviceNetwork = default!;
|
||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||
[Dependency] private readonly SharedChatSystem _chat = default!;
|
||||
[Dependency] protected readonly AccessReaderSystem AccessReader = default!;
|
||||
[Dependency] protected readonly ISharedAdminLogManager AdminLog = default!;
|
||||
[Dependency] private readonly SharedDVStationKeycardAuthenticationDeviceSystem _stationKeycardAuthenticationDevice = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DVCommunicationsConsoleComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<DVCommunicationsConsoleComponent, DeviceNetworkPacketEvent>(OnPacketReceive);
|
||||
Subs.BuiEvents<DVCommunicationsConsoleComponent>(DVCommunicationsConsoleUi.Key,
|
||||
subs =>
|
||||
{
|
||||
subs.Event<DVCommunicationsConsoleEvacuationShuttleMessage>(OnEvacuationShuttle);
|
||||
subs.Event<DVCommunicationsConsoleExfiltrationShuttleMessage>(OnExfiltrationShuttle);
|
||||
subs.Event<DVCommunicationsConsoleScreenConfigurationMessage>(OnConfiguration);
|
||||
subs.Event<DVCommunicationsConsoleAnnouncementMessage>(OnAnnouncement);
|
||||
subs.Event<DVCommunicationsConsoleAlertLevelMessage>(OnAlertLevel);
|
||||
subs.Event<DVCommunicationsConsoleKeycardAuthenticationDeviceMessage>(OnKeycardAuthenticationDevice);
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPacketReceive(Entity<DVCommunicationsConsoleComponent> ent, ref DeviceNetworkPacketEvent args)
|
||||
{
|
||||
if (args.Data.TryGetValue(DVScreenPackets.Text, out (string, string)? text))
|
||||
{
|
||||
ent.Comp.LastConfiguredLine1 = text.Value.Item1;
|
||||
ent.Comp.LastConfiguredLine2 = text.Value.Item2;
|
||||
Dirty(ent);
|
||||
}
|
||||
if (args.Data.TryGetValue(DVScreenPackets.ShowBorders, out bool? showBorders))
|
||||
{
|
||||
ent.Comp.LastConfiguredShowBorders = showBorders.Value;
|
||||
Dirty(ent);
|
||||
}
|
||||
if (args.Data.TryGetValue(DVScreenPackets.Content, out DVScreenContent? content))
|
||||
{
|
||||
ent.Comp.LastConfiguredContent = content.Value;
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnMapInit(Entity<DVCommunicationsConsoleComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.CanAnnounceAt = Timing.CurTime + ent.Comp.InitialAnnouncementDelay;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void OnAnnouncement(Entity<DVCommunicationsConsoleComponent> ent, ref DVCommunicationsConsoleAnnouncementMessage args)
|
||||
{
|
||||
if (!ent.Comp.CanAnnounce)
|
||||
return;
|
||||
|
||||
if (Timing.CurTime <= ent.Comp.CanAnnounceAt)
|
||||
return;
|
||||
|
||||
if (!AccessReader.IsAllowed(args.Actor, ent))
|
||||
return;
|
||||
|
||||
var identity = new TryGetIdentityShortInfoEvent(ent, args.Actor);
|
||||
RaiseLocalEvent(identity);
|
||||
|
||||
Loc.TryGetString(ent.Comp.AnnouncementTitle, out var title);
|
||||
title ??= ent.Comp.AnnouncementTitle;
|
||||
|
||||
var msg = args.Announcement;
|
||||
msg += "\n" + Loc.GetString("comms-console-announcement-sent-by") + " " + identity.Title;
|
||||
|
||||
if (ent.Comp.GlobalAnnouncements)
|
||||
{
|
||||
_chat.DispatchGlobalAnnouncement(msg, title, announcementSound: ent.Comp.AnnouncementSound, colorOverride: ent.Comp.AnnouncementColor);
|
||||
AdminLog.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(args.Actor):player} sent the following global announcement using {ToPrettyString(ent):console}: {msg:message}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_chat.DispatchStationAnnouncement(ent, msg, title, announcementSound: ent.Comp.AnnouncementSound, colorOverride: ent.Comp.AnnouncementColor);
|
||||
AdminLog.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(args.Actor):player} sent the following station announcement using {ToPrettyString(ent):console}: {msg:message}");
|
||||
}
|
||||
|
||||
ent.Comp.CanAnnounceAt = Timing.CurTime + ent.Comp.AnnouncementInterval;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void OnConfiguration(Entity<DVCommunicationsConsoleComponent> ent, ref DVCommunicationsConsoleScreenConfigurationMessage args)
|
||||
{
|
||||
if (!ent.Comp.CanConfigureScreens)
|
||||
return;
|
||||
|
||||
if (!AccessReader.IsAllowed(args.Actor, ent))
|
||||
return;
|
||||
|
||||
ent.Comp.LastConfiguredLine1 = args.Line1;
|
||||
ent.Comp.LastConfiguredLine2 = args.Line2;
|
||||
ent.Comp.LastConfiguredShowBorders = args.ShowBorder;
|
||||
ent.Comp.LastConfiguredContent = args.Content;
|
||||
Dirty(ent);
|
||||
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[DVScreenPackets.Content] = args.Content,
|
||||
[DVScreenPackets.ShowBorders] = args.ShowBorder,
|
||||
[DVScreenPackets.Text] = (args.Line1, args.Line2),
|
||||
};
|
||||
|
||||
AdminLog.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(args.Actor):player} configured the following text using {ToPrettyString(ent):console}: {args.Line1:line1} {args.Line2:line2}");
|
||||
_deviceNetwork.QueuePacket(ent, null, payload);
|
||||
}
|
||||
|
||||
protected virtual void OnExfiltrationShuttle(Entity<DVCommunicationsConsoleComponent> ent,
|
||||
ref DVCommunicationsConsoleExfiltrationShuttleMessage args)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnEvacuationShuttle(Entity<DVCommunicationsConsoleComponent> ent,
|
||||
ref DVCommunicationsConsoleEvacuationShuttleMessage args)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnAlertLevel(Entity<DVCommunicationsConsoleComponent> ent,
|
||||
ref DVCommunicationsConsoleAlertLevelMessage args)
|
||||
{
|
||||
}
|
||||
|
||||
private void OnKeycardAuthenticationDevice(Entity<DVCommunicationsConsoleComponent> ent,
|
||||
ref DVCommunicationsConsoleKeycardAuthenticationDeviceMessage args)
|
||||
{
|
||||
_stationKeycardAuthenticationDevice.TrySwipe(args.Actor, ent, args.Action);
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.KeycardAuthenticationDevice;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(SharedDVStationKeycardAuthenticationDeviceSystem))]
|
||||
public sealed partial class DVStationKeycardAuthenticationDeviceAlreadySwipedComponent : Component;
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared._DV.KeycardAuthenticationDevice;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||
[Access(typeof(SharedDVStationKeycardAuthenticationDeviceSystem))]
|
||||
public sealed partial class DVStationKeycardAuthenticationDeviceComponent : Component
|
||||
{
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField]
|
||||
public TimeSpan AccessibleAfter = TimeSpan.Zero;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan FailureDelay = TimeSpan.FromMinutes(2);
|
||||
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField]
|
||||
public TimeSpan? SwipesStartedAt = null;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan SwipeWindow = TimeSpan.FromSeconds(20);
|
||||
|
||||
[DataField(required: true)]
|
||||
public Dictionary<DVStationKeycardAction, int> ActionThresholds;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public int Swipes;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public DVStationKeycardAction? SwipingFor;
|
||||
|
||||
[DataField]
|
||||
public LocId FailureAnnouncementSender = "keycard-authentication-device-sender";
|
||||
|
||||
[DataField]
|
||||
public LocId FailureAnnouncement = "keycard-authentication-device-warning";
|
||||
|
||||
[DataField]
|
||||
public Color FailureAnnouncementColor = Color.FromHex("#e93a9a");
|
||||
|
||||
[DataField]
|
||||
public TimeSpan FailureElectrocutionDuration = TimeSpan.FromSeconds(5);
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DVStationKeycardAction
|
||||
{
|
||||
Mayday,
|
||||
Scuttling,
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Electrocution;
|
||||
using Content.Shared.Station;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._DV.KeycardAuthenticationDevice;
|
||||
|
||||
public abstract class SharedDVStationKeycardAuthenticationDeviceSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] protected readonly SharedStationSystem Station = default!;
|
||||
[Dependency] private readonly SharedElectrocutionSystem _electrocution = default!;
|
||||
[Dependency] private readonly SharedChatSystem _chat = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<DVStationKeycardAuthenticationDeviceComponent>();
|
||||
while (query.MoveNext(out var uid, out var keycard))
|
||||
{
|
||||
if (keycard.SwipesStartedAt is not { } startedAt || _timing.CurTime <= startedAt + keycard.SwipeWindow)
|
||||
continue;
|
||||
|
||||
StopSwipes((uid, keycard), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void StopSwipes(Entity<DVStationKeycardAuthenticationDeviceComponent> station, bool failed)
|
||||
{
|
||||
station.Comp.SwipesStartedAt = null;
|
||||
station.Comp.Swipes = 0;
|
||||
station.Comp.SwipingFor = null;
|
||||
if (failed)
|
||||
{
|
||||
station.Comp.AccessibleAfter = _timing.CurTime + station.Comp.FailureDelay;
|
||||
_chat.DispatchStationAnnouncement(station,
|
||||
Loc.GetString(station.Comp.FailureAnnouncement),
|
||||
Loc.GetString(station.Comp.FailureAnnouncementSender),
|
||||
announcementSound: new SoundPathSpecifier("/Audio/_DV/Announcements/attention.ogg"),
|
||||
colorOverride: station.Comp.FailureAnnouncementColor);
|
||||
}
|
||||
Dirty(station);
|
||||
|
||||
var query = EntityQueryEnumerator<DVStationKeycardAuthenticationDeviceAlreadySwipedComponent>();
|
||||
while (query.MoveNext(out var uid, out _))
|
||||
{
|
||||
if (failed)
|
||||
{
|
||||
_electrocution.TryDoElectrocution(uid,
|
||||
null,
|
||||
10,
|
||||
station.Comp.FailureElectrocutionDuration,
|
||||
true,
|
||||
ignoreInsulation: true);
|
||||
}
|
||||
RemCompDeferred<DVStationKeycardAuthenticationDeviceAlreadySwipedComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void Swipe(Entity<DVStationKeycardAuthenticationDeviceComponent> station, EntityUid user, DVStationKeycardAction action)
|
||||
{
|
||||
if (!station.Comp.ActionThresholds.TryGetValue(action, out var threshold))
|
||||
return;
|
||||
|
||||
station.Comp.SwipesStartedAt ??= _timing.CurTime;
|
||||
station.Comp.SwipingFor = action;
|
||||
station.Comp.Swipes++;
|
||||
AddComp<DVStationKeycardAuthenticationDeviceAlreadySwipedComponent>(user);
|
||||
Dirty(station);
|
||||
|
||||
if (station.Comp.Swipes >= threshold)
|
||||
{
|
||||
StopSwipes(station, false);
|
||||
DoAction(station, action);
|
||||
}
|
||||
}
|
||||
|
||||
private void DoAction(Entity<DVStationKeycardAuthenticationDeviceComponent> station, DVStationKeycardAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case DVStationKeycardAction.Mayday:
|
||||
Mayday(station);
|
||||
break;
|
||||
|
||||
case DVStationKeycardAction.Scuttling:
|
||||
Scuttling(station);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Mayday(Entity<DVStationKeycardAuthenticationDeviceComponent> station)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void Scuttling(Entity<DVStationKeycardAuthenticationDeviceComponent> station)
|
||||
{
|
||||
}
|
||||
|
||||
public void TrySwipe(EntityUid user, EntityUid console, DVStationKeycardAction action)
|
||||
{
|
||||
if (HasComp<DVStationKeycardAuthenticationDeviceAlreadySwipedComponent>(console))
|
||||
return;
|
||||
|
||||
if (Station.GetOwningStation(user) is not { } station || !TryComp<DVStationKeycardAuthenticationDeviceComponent>(station, out var keycard))
|
||||
return;
|
||||
|
||||
if (TryComp<AccessReaderComponent>(console, out var accessReader) && !_accessReader.IsAllowed(user, console, accessReader))
|
||||
return;
|
||||
|
||||
if (keycard.SwipingFor is { } swipingFor && swipingFor != action)
|
||||
return;
|
||||
|
||||
if (_timing.CurTime <= keycard.AccessibleAfter)
|
||||
return;
|
||||
|
||||
Swipe((station, keycard), user, action);
|
||||
}
|
||||
}
|
||||
@@ -70,13 +70,23 @@ public abstract class SharedLightReactiveSystem : EntitySystem
|
||||
/// Avoid calling this too often, as it can be expensive.
|
||||
/// </summary>
|
||||
public float GetLightLevelForPoint(EntityUid uid, TransformComponent? xform = null)
|
||||
{
|
||||
return GetLightLevelAtPosition(uid, _transform.GetWorldPosition(uid), xform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the light level at an arbitrary world position, using <paramref name="uid"/> for the
|
||||
/// light lookup and map resolution. Lets callers sample somewhere other than the entity's
|
||||
/// centre — e.g. a point just outside a wall, so the wall's own body can occlude the ray.
|
||||
/// Avoid calling this too often, as it can be expensive.
|
||||
/// </summary>
|
||||
public float GetLightLevelAtPosition(EntityUid uid, Vector2 pos, TransformComponent? xform = null)
|
||||
{
|
||||
float val = 0.0f;
|
||||
// Get the current map entity so we can get a MapLightComponent from it if it has one
|
||||
var map = _transform.GetMap((uid, xform));
|
||||
if (TryComp(map, out MapLightComponent? mapLight))
|
||||
val += (mapLight.AmbientLightColor.R + mapLight.AmbientLightColor.G + mapLight.AmbientLightColor.B) / 3f;
|
||||
var pos = _transform.GetWorldPosition(uid);
|
||||
|
||||
foreach (var (lightUid, lightComp) in GetLights(uid))
|
||||
{
|
||||
|
||||
@@ -187,14 +187,14 @@ public abstract class SharedNodeCrawlSystem : EntitySystem
|
||||
|
||||
private void OnMovementShutdown(Entity<NodeCrawlerMovementComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (ent.Comp.Node is { } node)
|
||||
if (ent.Comp.Node is { } node && node.IsValid())
|
||||
{
|
||||
var nodeComp = Comp<CrawlableNodeComponent>(node);
|
||||
nodeComp.Crawlers.Remove(ent);
|
||||
Dirty(node, nodeComp);
|
||||
}
|
||||
|
||||
if (ent.Comp.HeldCrawler is { } crawler && !TerminatingOrDeleted(crawler) && TryComp<NodeCrawlerComponent>(crawler, out var nodeCrawler))
|
||||
if (ent.Comp.HeldCrawler is { } crawler && crawler.IsValid() && !TerminatingOrDeleted(crawler) && TryComp<NodeCrawlerComponent>(crawler, out var nodeCrawler))
|
||||
{
|
||||
ExitNodeCrawl((crawler, nodeCrawler));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Overlays.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Gives the owner darkvision: lighting still renders, but total darkness is raised to
|
||||
/// <see cref="LightFloor"/> brightness instead of pitch black. Unlike night vision this keeps
|
||||
/// the whole lighting gradient visible, so creatures like the Skia can judge what is and isn't dark enough for them.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class DarkVisionComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Brightness that full darkness renders at, 0-1. Rendered light is clamped to a minimum of this value.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float LightFloor = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier applied to actual light on top of the floor. Values above 1 overbrighten lit areas so they are unmistakable next to the grey darkness floor.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float LightGain = 8f;
|
||||
|
||||
/// <summary>
|
||||
/// Exponent applied to lights, to make brighter areas look notably brighter
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float LightExp = 2f;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Screens;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause]
|
||||
[Access(typeof(DVSharedScreenSystem))]
|
||||
public sealed partial class DVScreenComponent : Component
|
||||
{
|
||||
[DataField, AutoNetworkedField]
|
||||
public string? AlertLevel; // I don't like this but uhhh the prototype isn't client-accessible
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool ShowAlertBorder;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public DVScreenContent Content = DVScreenContent.Text;
|
||||
|
||||
#region Text Screens
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public string Line1 = string.Empty;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public string Line2 = string.Empty;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ETA Screens
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool ScreenIsAtDestination;
|
||||
|
||||
[DataField, AutoNetworkedField, AutoPausedField]
|
||||
public TimeSpan TargetTime = TimeSpan.Zero;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DVScreenVisuals : byte
|
||||
{
|
||||
AlertLevel,
|
||||
ShowAlertBorder,
|
||||
Content,
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DVScreenContent : byte
|
||||
{
|
||||
Text,
|
||||
CurrentTime,
|
||||
EstimatedTimeOfArrival,
|
||||
AlertLevel,
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Content.Shared._DV.Screens;
|
||||
|
||||
public abstract class DVSharedScreenSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
protected void UpdateVisuals(Entity<DVScreenComponent> ent)
|
||||
{
|
||||
_appearance.SetData(ent.Owner, DVScreenVisuals.AlertLevel, ent.Comp.AlertLevel ?? string.Empty);
|
||||
_appearance.SetData(ent.Owner, DVScreenVisuals.ShowAlertBorder, ent.Comp.ShowAlertBorder);
|
||||
_appearance.SetData(ent.Owner, DVScreenVisuals.Content, ent.Comp.Content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Screens;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DVTextScreenVisualLayers : byte
|
||||
{
|
||||
Line1,
|
||||
Line2,
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._DV.ShadowWalk;
|
||||
|
||||
/// <summary>
|
||||
/// Lets this entity walk straight through solid static objects (walls, doors, windows...)
|
||||
/// while the entity itself is bathed in darkness (the same light level it heals in.)
|
||||
/// Mobs and projectiles always stay solid.
|
||||
/// <para>
|
||||
/// On collision, checks our light level. Objects we're stuck in are tagged in <see cref="PassableEntities"/>
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class ShadowWalkerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Light level below which an object counts as bathed in darkness.
|
||||
/// If the entity has a <c>LightLevelHealthComponent</c> its DarkThreshold is used
|
||||
/// instead, so objects are passable exactly where the entity would heal.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float DarkThreshold = 0.3f;
|
||||
|
||||
/// <summary>
|
||||
/// Objects we're currently in. Objects in this list are never solid until we fully leave.
|
||||
/// </summary>
|
||||
public HashSet<EntityUid> PassableEntities = new();
|
||||
|
||||
/// <summary>
|
||||
/// Light level for this tick, to avoid re-calculating for more than one collision a tick.
|
||||
/// </summary>
|
||||
public GameTick LastLightCheckTick = GameTick.Zero;
|
||||
|
||||
public float LastLightLevel;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using Content.Shared._DV.Body;
|
||||
using Content.Shared._DV.Light;
|
||||
using Content.Shared.Projectiles;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._DV.ShadowWalk;
|
||||
|
||||
/// <summary>
|
||||
/// Can walk through darkness freely.
|
||||
/// </summary>
|
||||
public sealed partial class SharedShadowWalkSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedLightReactiveSystem _lightReactive = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
/// <summary>
|
||||
/// How far outside a tagged object's AABB the walker's centre must be before the object is untagged (and so becomes solid again on the next collision).
|
||||
/// At least the walker's collision radius, so an object is never made solid while it still overlaps the walker.
|
||||
/// </summary>
|
||||
private const float UnstickMargin = 0.45f;
|
||||
|
||||
/// <summary>
|
||||
/// Gamefeel. Non-walls get a bigger unstick margin so they stay unstick even if you clip into a wall. Prevents getting stuck in walls.
|
||||
/// </summary>
|
||||
private const float MovableUnstickMargin = 1f;
|
||||
|
||||
private EntityQuery<LightLevelHealthComponent> _lightHealthQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<ProjectileComponent> _projectileQuery;
|
||||
|
||||
private readonly List<EntityUid> _toRemove = [];
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_lightHealthQuery = GetEntityQuery<LightLevelHealthComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_projectileQuery = GetEntityQuery<ProjectileComponent>();
|
||||
|
||||
SubscribeLocalEvent<ShadowWalkerComponent, PreventCollideEvent>(OnPreventCollide);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var query = EntityQueryEnumerator<ShadowWalkerComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (comp.PassableEntities.Count == 0)
|
||||
continue;
|
||||
|
||||
var worldPos = _transform.GetWorldPosition(uid);
|
||||
|
||||
_toRemove.Clear();
|
||||
foreach (var other in comp.PassableEntities)
|
||||
{
|
||||
// Untag anything we've deleted or fully walked clear of; the next collision with it will re-check the light level from scratch.
|
||||
if (Deleted(other))
|
||||
{
|
||||
_toRemove.Add(other);
|
||||
continue;
|
||||
}
|
||||
|
||||
var margin = UnstickMargin;
|
||||
// Non-statics get a bigger margin :)
|
||||
if (_physicsQuery.TryComp(other, out var body) && body.BodyType != BodyType.Static)
|
||||
margin += MovableUnstickMargin;
|
||||
|
||||
if (!_lookup.GetWorldAABB(other).Enlarged(margin).Contains(worldPos))
|
||||
_toRemove.Add(other);
|
||||
}
|
||||
|
||||
foreach (var other in _toRemove)
|
||||
comp.PassableEntities.Remove(other);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPreventCollide(Entity<ShadowWalkerComponent> ent, ref PreventCollideEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
// Only phase through hard blockers; sensor fixtures must keep triggering.
|
||||
if (!args.OurFixture.Hard || !args.OtherFixture.Hard)
|
||||
return;
|
||||
|
||||
// Already phasing through this one: keep it passable until we've left it (pruned in
|
||||
// Update), so a light change mid-overlap can never trap us inside it.
|
||||
if (ent.Comp.PassableEntities.Contains(args.OtherEntity))
|
||||
{
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CanPhaseThrough(args.OtherEntity, args.OtherBody))
|
||||
return;
|
||||
|
||||
// A fresh collision: only phase if the walker itself is currently in darkness.
|
||||
if (!InDarkness(ent))
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
ent.Comp.PassableEntities.Add(args.OtherEntity);
|
||||
}
|
||||
|
||||
private bool CanPhaseThrough(EntityUid other, PhysicsComponent otherBody)
|
||||
{
|
||||
if (otherBody.BodyType == BodyType.KinematicController)
|
||||
return false;
|
||||
// Bullets never pass or hit based on collision timing.
|
||||
if (_projectileQuery.HasComp(other))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool InDarkness(Entity<ShadowWalkerComponent> ent)
|
||||
{
|
||||
// Darkness is whatever the walker heals in, if it heals in darkness at all.
|
||||
var threshold = _lightHealthQuery.TryComp(ent, out var lightHealth)
|
||||
? lightHealth.DarkThreshold
|
||||
: ent.Comp.DarkThreshold;
|
||||
|
||||
var curTick = _timing.CurTick;
|
||||
if (ent.Comp.LastLightCheckTick != curTick)
|
||||
{
|
||||
ent.Comp.LastLightLevel = _lightReactive.GetLightLevelForPoint(ent.Owner);
|
||||
ent.Comp.LastLightCheckTick = curTick;
|
||||
}
|
||||
|
||||
return ent.Comp.LastLightLevel < threshold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Robust.Shared.Configuration;
|
||||
using Content.Shared.CCVar.CVarAccess;
|
||||
using Content.Shared.Administration;
|
||||
|
||||
namespace Content.Shared.Starlight.CCVar;
|
||||
|
||||
[CVarDefs]
|
||||
public sealed partial class StarlightCCVars
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Shared.Starlight.CCVar;
|
||||
|
||||
public sealed partial class StarlightCCVars
|
||||
{
|
||||
/// <summary>
|
||||
/// A newline-separated list of saved labels for the hand labeler tool
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> HandLabelerSavedLabels =
|
||||
CVarDef.Create("interface.hand_labeler_saved_labels", "", CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
- files: ["gnome_scream.ogg", "gnomedeath.ogg", "gnomelaugh.ogg", "gnome1.ogg"]
|
||||
license: "CC-BY-SA-4.0"
|
||||
copyright: "Audio recorded by Cepelinas and edited by rebe83"
|
||||
source: "https://github.com/DeltaV-Station/Delta-v/pull/6027"
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -356,5 +356,29 @@ Entries:
|
||||
id: 39
|
||||
time: '2026-07-24T17:27:48.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6351
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: Curators, get off your lazy asses and help out the newbies.
|
||||
type: Tweak
|
||||
id: 40
|
||||
time: '2026-08-09T18:14:15.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6374
|
||||
- author: DisposableCrewmember42
|
||||
changes:
|
||||
- message: Added Unorbitable component, which prevents entities from being followed/warped
|
||||
to.
|
||||
type: Add
|
||||
id: 41
|
||||
time: '2026-08-22T16:18:32.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6435
|
||||
- author: einknusprigestoast
|
||||
changes:
|
||||
- message: ERT elite hardsuits now have different values
|
||||
type: Tweak
|
||||
- message: added new guns to ERT gamma and amber loadouts
|
||||
type: Add
|
||||
id: 42
|
||||
time: '2026-09-09T14:07:35.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6239
|
||||
Name: DeltaVAdmin
|
||||
Order: 5
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1087,4 +1087,18 @@
|
||||
id: 125
|
||||
time: '2026-07-14T00:30:06.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6242
|
||||
- author: Field Command
|
||||
changes:
|
||||
- message: 'Central Command: New CC is here!'
|
||||
type: Add
|
||||
id: 126
|
||||
time: '2026-09-05T14:03:09.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6198
|
||||
- author: BriBrooo
|
||||
changes:
|
||||
- message: Added a light gray version of the air sensor.
|
||||
type: Add
|
||||
id: 127
|
||||
time: '2026-09-17T03:01:35.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6340
|
||||
Order: 3
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
[game]
|
||||
hostname = "[EN][MRP] Delta-v (Ψ) | Apoapsis [US East 1]"
|
||||
hostname = "[EN][MRP] Delta-V (Ψ) | Apoapsis [NA East 1]"
|
||||
soft_max_players = 100
|
||||
|
||||
[adminlogs]
|
||||
server_name = "Apoapsis"
|
||||
|
||||
[hub]
|
||||
tags = "lang:en-US,region:am_n_e,rp:med,no_tag_infer"
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ time = 600
|
||||
|
||||
[chat]
|
||||
max_announcement_length = 512
|
||||
rate_limit_announce_admins_delay = -1
|
||||
|
||||
[audio]
|
||||
attenuation = 4 # inversedistanceclamped 1 << 2, see audioparams.cs
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
[game]
|
||||
hostname = "[EN][MRP] Delta-v (Ψ) | Horizon [US East 3]"
|
||||
hostname = "[EN][MRP] Delta-V (Ψ) | Horizon [NA East 3]"
|
||||
soft_max_players = 80
|
||||
|
||||
[adminlogs]
|
||||
server_name = "Horizon"
|
||||
|
||||
[vote]
|
||||
preset_enabled = true
|
||||
map_enabled = true
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user