Compare commits

..
1 Commits
Author SHA1 Message Date
Velcroboy 4d0399e364 TEST 2026-07-08 13:08:08 -05:00
1746 changed files with 53843 additions and 440437 deletions
+19 -10
View File
@@ -1,25 +1,34 @@
# Last match in file takes precedence. # 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
* @DeltaV-Station/maintainers # C# code
/Content.*/ @DeltaV-Station/maintainers
# Any assets
/Resources/ @DeltaV-Station/maintainers
# Server config files # Server config files
/Resources/ConfigPresets/ @DeltaV-Station/lead-maintainers /Resources/ConfigPresets/ @Toby222
# Github workflows
/.github/ @DeltaV-Station/lead-maintainers
# Lobby art and music - automatically direction issues since its immediately visible to players # Lobby art and music - automatically direction issues since its immediately visible to players
/Resources/Audio/Lobby/ @DeltaV-Station/direction @DeltaV-Station/maintainers /Resources/Audio/Lobby/ @DeltaV-Station/direction
/Resources/Textures/LobbyScreens/ @DeltaV-Station/direction @DeltaV-Station/maintainers /Resources/Textures/LobbyScreens/ @DeltaV-Station/direction
# Maps # Maps
/Resources/Maps/ @DeltaV-Station/maptainers /Resources/Maps/ @DeltaV-Station/maptainers
/Resources/Prototypes/Maps/ @DeltaV-Station/maptainers /Resources/Prototypes/Maps/ @DeltaV-Station/maptainers
/Content.IntegrationTests/Tests/PostMapInitTest.cs @DeltaV-Station/maptainers @DeltaV-Station/maintainers /Content.IntegrationTests/Tests/PostMapInitTest.cs @DeltaV-Station/maptainers
# Server rules # Server rules
/Resources/ServerInfo/Guidebook/_DV/Rules/ @DeltaV-Station/head-administrators /Resources/ServerInfo/Guidebook/_DV/Rules/ @DeltaV-Station/head-administrators
# Wiki # Wiki
/.wiki/ @DeltaV-Station/direction /.wiki/ @DeltaV-Station/direction
# Tools and scripts
/Tools/ @Toby222
# Workflows, codeowners, templates, etc.
/.github/ @Toby222
# Standalone files in the root repo
/* @Toby222
+1 -1
View File
@@ -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: - [ ] 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] 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 --> - [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** **Changelog**
<!-- Add a Changelog entry to make players aware of new features or changes that could affect gameplay. <!-- Add a Changelog entry to make players aware of new features or changes that could affect gameplay.
@@ -2,7 +2,6 @@ using Content.Client.Cargo.UI;
using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Content.Shared._DV.Cargo.Components; // DeltaV: Bounty claim messages
namespace Content.Client.Cargo.BUI; namespace Content.Client.Cargo.BUI;
@@ -31,18 +30,6 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
{ {
SendMessage(new BountySkipMessage(id)); SendMessage(new BountySkipMessage(id));
}; };
// DeltaV: bounty claim stuff begins
_menu.OnClaimButtonPressed += id =>
{
SendMessage(new BountyClaimedMessage(id));
};
_menu.OnStatusOptionSelected += (id, status) =>
{
SendMessage(new BountySetStatusMessage(id, status));
};
// DeltaV: bounty claim stuff ends
} }
protected override void UpdateState(BoundUserInterfaceState message) protected override void UpdateState(BoundUserInterfaceState message)
+4 -14
View File
@@ -10,8 +10,6 @@
<BoxContainer Orientation="Vertical" HorizontalExpand="True"> <BoxContainer Orientation="Vertical" HorizontalExpand="True">
<RichTextLabel Name="RewardLabel"/> <RichTextLabel Name="RewardLabel"/>
<RichTextLabel Name="ManifestLabel"/> <RichTextLabel Name="ManifestLabel"/>
<RichTextLabel Name="ClaimedBylabel"/> <!-- DeltaV bounty claim -->
<RichTextLabel Name="StatusLabel"/> <!-- DeltaV bounty claim -->
</BoxContainer> </BoxContainer>
<Control MinWidth="10"/> <Control MinWidth="10"/>
<BoxContainer Orientation="Vertical" MinWidth="120"> <BoxContainer Orientation="Vertical" MinWidth="120">
@@ -19,22 +17,14 @@
<Button Name="PrintButton" <Button Name="PrintButton"
Text="{Loc 'bounty-console-label-button-text'}" Text="{Loc 'bounty-console-label-button-text'}"
HorizontalExpand="False" HorizontalExpand="False"
StyleClasses="OpenBoth"/> HorizontalAlignment="Right"
StyleClasses="OpenRight"/>
<Button Name="SkipButton" <Button Name="SkipButton"
Text="{Loc 'bounty-console-skip-button-text'}" Text="{Loc 'bounty-console-skip-button-text'}"
HorizontalExpand="True" HorizontalExpand="False"
HorizontalAlignment="Right"
StyleClasses="OpenLeft"/> StyleClasses="OpenLeft"/>
</BoxContainer> </BoxContainer>
<BoxContainer Orientation="Horizontal" MinWidth="120"> <!-- Begin DeltaV bounty claim content -->
<Button Name="ClaimButton"
Text= "{Loc 'bounty-console-claim-button-text'}"
HorizontalExpand="False"
StyleClasses="OpenRight"/>
<OptionButton Name="BountyStatusSelector"
Access="Public"
StyleClasses="OpenBoth"
HorizontalExpand="True"/>
</BoxContainer> <!-- End DeltaV content-->
<RichTextLabel Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/> <RichTextLabel Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/>
</BoxContainer> </BoxContainer>
</BoxContainer> </BoxContainer>
@@ -17,8 +17,6 @@ public sealed partial class BountyEntry : BoxContainer
public Action? OnLabelButtonPressed; public Action? OnLabelButtonPressed;
public Action? OnSkipButtonPressed; public Action? OnSkipButtonPressed;
public Action? OnClaimButtonPressed; // DeltaV
public Action? OnStatusOptionSelected; // DeltaV
public TimeSpan EndTime; public TimeSpan EndTime;
public TimeSpan UntilNextSkip; public TimeSpan UntilNextSkip;
@@ -47,20 +45,6 @@ public sealed partial class BountyEntry : BoxContainer
PrintButton.OnPressed += _ => OnLabelButtonPressed?.Invoke(); PrintButton.OnPressed += _ => OnLabelButtonPressed?.Invoke();
SkipButton.OnPressed += _ => OnSkipButtonPressed?.Invoke(); SkipButton.OnPressed += _ => OnSkipButtonPressed?.Invoke();
// Begin DeltaV bounty claiming
ClaimButton.OnPressed += _ => OnClaimButtonPressed?.Invoke();
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.Undelivered)}"), 0);
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.Waiting)}"), 1);
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.OnShuttle)}"), 2);
BountyStatusSelector.Select((int) bounty.Status);
BountyStatusSelector.ToolTip = Loc.GetString($"bounty-console-status-tooltip-{bounty.Status.ToString()}");
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-status-formatted-{bounty.Status.ToString()}"));
// End DeltaV bounty claiming
} }
private void UpdateSkipButton(float deltaSeconds) private void UpdateSkipButton(float deltaSeconds)
+2 -3
View File
@@ -1,9 +1,8 @@
<!--DeltaV - SetSize was 550, now 750 to accomodate bounty claim UI --> <controls:FancyWindow xmlns="https://spacestation14.io"
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client" xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'bounty-console-menu-title'}" Title="{Loc 'bounty-console-menu-title'}"
SetSize="750 420" SetSize="550 420"
MinSize="400 350"> MinSize="400 350">
<BoxContainer Orientation="Vertical" <BoxContainer Orientation="Vertical"
VerticalExpand="True" VerticalExpand="True"
@@ -11,8 +11,6 @@ public sealed partial class CargoBountyMenu : FancyWindow
{ {
public Action<string>? OnLabelButtonPressed; public Action<string>? OnLabelButtonPressed;
public Action<string>? OnSkipButtonPressed; public Action<string>? OnSkipButtonPressed;
public Action<string>? OnClaimButtonPressed; // DeltaV
public Action<string, int>? OnStatusOptionSelected; // DeltaV
public CargoBountyMenu() public CargoBountyMenu()
{ {
@@ -33,8 +31,6 @@ public sealed partial class CargoBountyMenu : FancyWindow
var entry = new BountyEntry(b, untilNextSkip); var entry = new BountyEntry(b, untilNextSkip);
entry.OnLabelButtonPressed += () => OnLabelButtonPressed?.Invoke(b.Id); entry.OnLabelButtonPressed += () => OnLabelButtonPressed?.Invoke(b.Id);
entry.OnSkipButtonPressed += () => OnSkipButtonPressed?.Invoke(b.Id); entry.OnSkipButtonPressed += () => OnSkipButtonPressed?.Invoke(b.Id);
entry.OnClaimButtonPressed += () => OnClaimButtonPressed?.Invoke(b.Id); // DeltaV
entry.BountyStatusSelector.OnItemSelected += args => OnStatusOptionSelected?.Invoke(b.Id, args.Id); // DeltaV
BountyEntriesContainer.AddChild(entry); BountyEntriesContainer.AddChild(entry);
} }
@@ -28,6 +28,7 @@ namespace Content.Client.Communications.UI
_menu.OnBroadcast += BroadcastButtonPressed; _menu.OnBroadcast += BroadcastButtonPressed;
_menu.OnAlertLevel += AlertLevelSelected; _menu.OnAlertLevel += AlertLevelSelected;
_menu.OnEmergencyLevel += EmergencyShuttleButtonPressed; _menu.OnEmergencyLevel += EmergencyShuttleButtonPressed;
_menu.OnExfiltrationLevel += ExfiltrationShuttleButtonPressed; // DeltaV - Exfiltration shuttle
} }
public void AlertLevelSelected(string level) public void AlertLevelSelected(string level)
@@ -47,6 +48,13 @@ namespace Content.Client.Communications.UI
CallShuttle(); CallShuttle();
} }
// Begin DeltaV - Exfiltration Shuttle
public void ExfiltrationShuttleButtonPressed()
{
SendMessage(new CommunicationsConsoleExfiltrationShuttleMessage(!_menu!.CountdownStarted));
}
// End DeltaV - Exfiltration Shuttle
public void AnnounceButtonPressed(string message) public void AnnounceButtonPressed(string message)
{ {
var maxLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength); var maxLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength);
@@ -86,6 +94,13 @@ namespace Content.Client.Communications.UI
_menu.CurrentLevel = commsState.CurrentAlert; _menu.CurrentLevel = commsState.CurrentAlert;
_menu.CountdownEnd = commsState.ExpectedCountdownEnd; _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.UpdateCountdown();
_menu.UpdateAlertLevels(commsState.AlertLevels, _menu.CurrentLevel); _menu.UpdateAlertLevels(commsState.AlertLevels, _menu.CurrentLevel);
_menu.AlertLevelButton.Disabled = !_menu.AlertLevelSelectable; _menu.AlertLevelButton.Disabled = !_menu.AlertLevelSelectable;
@@ -52,10 +52,19 @@
<RichTextLabel Name="CountdownLabel"/> <RichTextLabel Name="CountdownLabel"/>
<!-- DeltaV - Exfiltration Shuttle -->
<RichTextLabel Name="ExfiltrationCountdownLabel"/>
<Button Name="EmergencyShuttleButton" <Button Name="EmergencyShuttleButton"
Access="Public" Access="Public"
Text="Placeholder Text" Text="Placeholder Text"
ToolTip="{Loc 'comms-console-menu-emergency-shuttle-button-tooltip'}"/> 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> </BoxContainer>
</BoxContainer> </BoxContainer>
@@ -29,6 +29,12 @@ namespace Content.Client.Communications.UI
public event Action<string>? OnAnnounce; public event Action<string>? OnAnnounce;
public event Action<string>? OnBroadcast; 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() public CommunicationsConsoleMenu()
{ {
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
@@ -72,12 +78,18 @@ namespace Content.Client.Communications.UI
EmergencyShuttleButton.OnPressed += _ => OnEmergencyLevel?.Invoke(); EmergencyShuttleButton.OnPressed += _ => OnEmergencyLevel?.Invoke();
EmergencyShuttleButton.Disabled = !CanCall; EmergencyShuttleButton.Disabled = !CanCall;
// Begin DeltaV - Exfiltration Shuttle
ExfiltrationShuttleButton.OnPressed += _ => OnExfiltrationLevel?.Invoke();
ExfiltrationShuttleButton.Disabled = !CanExfiltrate;
// End DeltaV - Exfiltration Shuttle
} }
protected override void FrameUpdate(FrameEventArgs args) protected override void FrameUpdate(FrameEventArgs args)
{ {
base.FrameUpdate(args); base.FrameUpdate(args);
UpdateCountdown(); UpdateCountdown();
UpdateExfiltrationCountdown(); // DeltaV - Exfiltration shuttle
} }
// The current alert could make levels unselectable, so we need to ensure that the UI reacts properly. // The current alert could make levels unselectable, so we need to ensure that the UI reacts properly.
@@ -117,6 +129,25 @@ 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() public void UpdateCountdown()
{ {
if (!CountdownStarted) if (!CountdownStarted)
@@ -59,12 +59,9 @@
<Label Text="{Loc 'health-analyzer-window-entity-temperature-text'}" /> <Label Text="{Loc 'health-analyzer-window-entity-temperature-text'}" />
<Label Name="TemperatureLabel" /> <Label Name="TemperatureLabel" />
<Label Text="{Loc 'health-analyzer-window-entity-blood-level-text'}" /> <Label Text="{Loc 'health-analyzer-window-entity-blood-level-text'}" />
<!-- Begin DeltaV - Health Analyzer Plus --> <Label Name="BloodLabel" />
<Label Name="BloodLevelLabel" /> <Label Text="{Loc 'health-analyzer-window-entity-damage-total-text'}" />
<!-- Label Name="BloodLabel" / --> <Label Name="DamageLabel" />
<!-- Label Text="{Loc 'health-analyzer-window-entity-damage-total-text'}" / -->
<!-- Label Name="DamageLabel" / -->
<!-- End DeltaV - Health Analyzer Plus -->
</GridContainer> </GridContainer>
</BoxContainer> </BoxContainer>
@@ -76,41 +73,10 @@
<PanelContainer StyleClasses="LowDivider" /> <PanelContainer StyleClasses="LowDivider" />
<!-- Begin DeltaV - Health Analyzer Plus --> <BoxContainer
<!-- BoxContainer Name="GroupsContainer" Margin="0 5 0 5" Orientation="Vertical" --> Name="GroupsContainer"
<!-- /BoxContainer --> Margin="0 5 0 5"
Orientation="Vertical">
<GridContainer Margin="0 5 0 5" Columns="3" HorizontalAlignment="Center"> </BoxContainer>
<BoxContainer Name="DamageBox" Margin="5 5 5 5" Orientation="Vertical">
<Label Name="TotalDamageLabel" HorizontalAlignment="Center" />
<PanelContainer Name="DamageGroupsDivider" StyleClasses="LowDivider" />
<Label
Name="NoDamageLabel"
HorizontalAlignment="Center"
Text="{Loc 'health-analyzer-plus-window-no-damage-text'}"
FontColorOverride="DeepSkyBlue"
Margin="0 5 0 5" />
<BoxContainer HorizontalAlignment="Center">
<BoxContainer Name="DamageGroupsContainer" Margin="0 5 0 5" Orientation="Vertical" />
</BoxContainer>
</BoxContainer>
<PanelContainer StyleClasses="LowDivider" />
<BoxContainer Name="BloodstreamBox" Margin="5 5 5 5" Orientation="Vertical">
<Label Name="TotalReagentQuantityLabel" HorizontalAlignment="Center" />
<PanelContainer Name="BloodstreamReagentsDivider" StyleClasses="LowDivider" />
<Label
Name="NoReagentsLabel"
HorizontalAlignment="Center"
Text="{Loc 'health-analyzer-plus-window-no-reagents-text'}"
FontColorOverride="DeepSkyBlue"
Margin="0 5 0 5" />
<BoxContainer HorizontalAlignment="Center">
<BoxContainer Name="BloodstreamReagentListContainer" Orientation="Vertical" Margin="0 5 0 5" />
</BoxContainer>
</BoxContainer>
</GridContainer>
<!-- End DeltaV - Health Analyzer Plus -->
</BoxContainer> </BoxContainer>
@@ -19,25 +19,12 @@ using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
// BEGIN DeltaV
using Content.Client._DV.Traits.Assorted;
using Content.Shared._DV.Traits.Assorted;
using Content.Shared._DV.Medical; // Uncloneable
using Content.Shared._DV.MedicalRecords; // Medical Records
// Health Analyzer Plus
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Body.Systems;
using Content.Client.Body.Systems;
using Content.Shared.Chemistry.EntitySystems;
using Content.Client.Chemistry.Containers.EntitySystems;
using Content.Shared.Body.Components;
// END DeltaV
namespace Content.Client.HealthAnalyzer.UI; namespace Content.Client.HealthAnalyzer.UI;
using Content.Client._DV.Traits.Assorted; // DeltaV
using Content.Shared._DV.Traits.Assorted; // DeltaV
using Content.Shared._DV.Medical; // DeltaV - Uncloneable
using Content.Shared._DV.MedicalRecords; // DeltaV - Medical Records
// Health analyzer UI is split from its window because it's used by both the // Health analyzer UI is split from its window because it's used by both the
// health analyzer item and the cryo pod UI. // health analyzer item and the cryo pod UI.
@@ -45,8 +32,6 @@ namespace Content.Client.HealthAnalyzer.UI;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class HealthAnalyzerControl : BoxContainer public sealed partial class HealthAnalyzerControl : BoxContainer
{ {
private const string REAGENT_GROUP_MEDICINE = "Medicine"; // DeltaV - Health Analyzer
private readonly IEntityManager _entityManager; private readonly IEntityManager _entityManager;
private readonly SpriteSystem _spriteSystem; private readonly SpriteSystem _spriteSystem;
private readonly IPrototypeManager _prototypes; private readonly IPrototypeManager _prototypes;
@@ -56,8 +41,6 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
private readonly UnborgableSystem _unborgable; // DeltaV private readonly UnborgableSystem _unborgable; // DeltaV
private readonly RedshirtSystem _redshirt; // DeltaV private readonly RedshirtSystem _redshirt; // DeltaV
private readonly UncloneableSystem _uncloneable; // DeltaV private readonly UncloneableSystem _uncloneable; // DeltaV
private readonly SharedBloodstreamSystem _bloodstream; // DeltaV
private readonly SharedSolutionContainerSystem _solutionContainer; // DeltaV
// Begin DeltaV - Medical Records // Begin DeltaV - Medical Records
private readonly ButtonGroup _triageStatusGroup = new(); private readonly ButtonGroup _triageStatusGroup = new();
@@ -83,8 +66,6 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
_unborgable = _entityManager.System<UnborgableSystem>(); // DeltaV _unborgable = _entityManager.System<UnborgableSystem>(); // DeltaV
_redshirt = _entityManager.System<RedshirtSystem>(); // DeltaV _redshirt = _entityManager.System<RedshirtSystem>(); // DeltaV
_uncloneable = _entityManager.System<UncloneableSystem>(); // DeltaV _uncloneable = _entityManager.System<UncloneableSystem>(); // DeltaV
_bloodstream = _entityManager.System<BloodstreamSystem>(); // DeltaV
_solutionContainer = _entityManager.System<SolutionContainerSystem>(); // DeltaV
// Begin DeltaV - Medical Records // Begin DeltaV - Medical Records
foreach (var item in Enum.GetValues<TriageStatus>()) foreach (var item in Enum.GetValues<TriageStatus>())
@@ -157,13 +138,9 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
? $"{state.Temperature - Atmospherics.T0C:F1} °C ({state.Temperature:F1} K)" ? $"{state.Temperature - Atmospherics.T0C:F1} °C ({state.Temperature:F1} K)"
: Loc.GetString("health-analyzer-window-entity-unknown-value-text"); : Loc.GetString("health-analyzer-window-entity-unknown-value-text");
// Begin DeltaV - Health Analyzer Plus
/*
BloodLabel.Text = !float.IsNaN(state.BloodLevel) BloodLabel.Text = !float.IsNaN(state.BloodLevel)
? $"{state.BloodLevel * 100:F1} %" ? $"{state.BloodLevel * 100:F1} %"
: Loc.GetString("health-analyzer-window-entity-unknown-value-text"); : Loc.GetString("health-analyzer-window-entity-unknown-value-text");
*/
// End DeltaV - Health Analyzer Plus
StatusLabel.Text = StatusLabel.Text =
_entityManager.TryGetComponent<MobStateComponent>(target.Value, out var mobStateComponent) _entityManager.TryGetComponent<MobStateComponent>(target.Value, out var mobStateComponent)
@@ -172,19 +149,16 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
// Total Damage // Total Damage
// DamageLabel.Text = _damageable.GetTotalDamage(target.Value).ToString(); // DeltaV - Health Analyzer Plus DamageLabel.Text = _damageable.GetTotalDamage(target.Value).ToString();
// Alerts // Alerts
// DeltaV traits - This is going to be horrid if we just keep adding things like this. // DeltaV traits - This is going to be horrid if we just keep adding things like this.
var unborgable = _unborgable.IsUnborgable(target.Value); var unborgable = _unborgable.IsUnborgable(target.Value);
var redshirt = _redshirt.IsRedshirt(target.Value) && mobStateComponent?.CurrentState == MobState.Dead; // DeltaV - Redshirt var redshirt = _redshirt.IsRedshirt(target.Value) && mobStateComponent?.CurrentState == MobState.Dead; // DeltaV - Redshirt
var uncloneable = _uncloneable.IsUncloneable(target.Value) && mobStateComponent?.CurrentState == MobState.Dead; // DeltaV - Unclonable 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 // END DeltaV
var showAlerts = state.Unrevivable == true || state.Bleeding == true || extraTraitAlerts; // DeltaV - extra alerts var showAlerts = state.Unrevivable == true || state.Bleeding == true;
AlertsDivider.Visible = showAlerts; AlertsDivider.Visible = showAlerts;
AlertsContainer.Visible = showAlerts; AlertsContainer.Visible = showAlerts;
@@ -242,7 +216,6 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
var damagePerType = _damageable.GetAllDamage(target.Value).DamageDict; var damagePerType = _damageable.GetAllDamage(target.Value).DamageDict;
DrawDiagnosticGroups(damageSortedGroups, damagePerType); DrawDiagnosticGroups(damageSortedGroups, damagePerType);
DrawBloodstreamInfo(target.Value, state.BloodSolution, state.BloodLevel); // End DeltaV - Health Analyzer Plus
// Begin DeltaV - Medical Records // Begin DeltaV - Medical Records
if (state.MedicalRecord is not { } records) if (state.MedicalRecord is not { } records)
@@ -281,15 +254,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2> groups, Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2> groups,
IReadOnlyDictionary<ProtoId<DamageTypePrototype>, FixedPoint2> damageDict) IReadOnlyDictionary<ProtoId<DamageTypePrototype>, FixedPoint2> damageDict)
{ {
// Begin DeltaV - Health Analyzer Plus GroupsContainer.RemoveAllChildren();
// GroupsContainer.RemoveAllChildren();
DamageGroupsContainer.RemoveAllChildren();
// Begin DeltaV - Health Analyzer Plus
var totalDamage = damageDict.Sum(x => x.Value.Double());
TotalDamageLabel.Text = Loc.GetString("health-analyzer-plus-window-entity-total-damage-text", ("amount", Math.Round(totalDamage, 2)));
NoDamageLabel.Visible = totalDamage == 0;
// End DeltaV - Health Analyzer Plus
foreach (var (damageGroupId, damageAmount) in groups) foreach (var (damageGroupId, damageAmount) in groups)
{ {
@@ -310,8 +275,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId)); groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId));
// GroupsContainer.AddChild(groupContainer); // DeltaV - Health Analyzer Plus GroupsContainer.AddChild(groupContainer);
DamageGroupsContainer.AddChild(groupContainer); // DeltaV - Health Analyzer Plus
// Show the damage for each type in that group. // Show the damage for each type in that group.
var group = _prototypes.Index(damageGroupId); var group = _prototypes.Index(damageGroupId);
@@ -332,116 +296,6 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
} }
} }
// Begin DeltaV - Health Analyzer Plus
private void DrawBloodstreamInfo(EntityUid target, Solution? bloodSolution, float bloodlevel)
{
BloodstreamReagentListContainer.RemoveAllChildren();
List<ReagentQuantity> bloodstream = new();
double totalReagentQuantity = 0;
double unknownReagents = 0;
// Make sure we can access the bloodtype and reagents before trying to use them
if (bloodSolution is not null)
{
if (!_entityManager.TryGetComponent<BloodstreamComponent>(target, out var bloodstreamComp) ||
!_solutionContainer.ResolveSolution(target, bloodstreamComp.BloodSolutionName, ref bloodstreamComp.BloodSolution, out var bloodReagentSolution))
return;
// Get the blood reagent IDs to ignore in the scan
var bloodReagentIds = bloodstreamComp.BloodReferenceSolution.Select(b => b.Reagent);
// Get all of the medicines in the bloodstream
foreach (var reagent in bloodSolution!.Contents)
{
if (bloodReagentIds.Contains(reagent.Reagent))
continue;
if (_prototypes.Index<ReagentPrototype>(reagent.Reagent.Prototype).Group == REAGENT_GROUP_MEDICINE)
{
bloodstream.Add(reagent);
totalReagentQuantity += reagent.Quantity.Double();
}
else
{
unknownReagents += reagent.Quantity.Double();
}
}
}
var bloodQuantityPercent = !float.IsNaN(bloodlevel)
? $"{bloodlevel * 100:F1} %"
: Loc.GetString("health-analyzer-window-entity-unknown-value-text");
// Display the percent and amount of blood in the system
// Normal health scanner is percent only, this one adds the actual units too
BloodLevelLabel.Text = Loc.GetString(
"health-analyzer-plus-window-entity-blood-level-quantity-text",
("percent", bloodQuantityPercent)
);
// Display the total amount of reagents in the bloodstream, 0 is valid
TotalReagentQuantityLabel.Text = Loc.GetString(
"health-analyzer-plus-window-entity-total-reagents-text",
("amount", Math.Round(totalReagentQuantity, 1).ToString("0.0"))
);
// If the bloodstream was empty, display a message instead to fill the space with something
NoReagentsLabel.Visible = bloodstream.Count == 0 && unknownReagents == 0;
// For each of the reagents in the bloodstream, add them to the list of reagents
// If there are none, this loop will simply do nothing
foreach (var (reagentID, reagentQuantity) in bloodstream)
{
var reagentPrototype = _prototypes.Index<ReagentPrototype>(reagentID.Prototype);
// Make a new container for each reagent we add to the list
var reagentContainer = new BoxContainer
{
Align = AlignMode.Begin,
Orientation = LayoutOrientation.Vertical
};
// Add the string "[] <reagent_name>: <reagent_amount>u" for the current reagent
// The [] is a unicode block character, and is colored with the reagent color
// to make it easier to identify
reagentContainer.AddChild(
new RichTextLabel
{
Text = Loc.GetString("health-analyzer-plus-window-reagent-text",
("reagentColor", reagentPrototype.SubstanceColor),
("reagentName", reagentPrototype.LocalizedName),
("amount", Math.Round(reagentQuantity.Double(), 1).ToString("0.0"))
)
}
);
BloodstreamReagentListContainer.AddChild(reagentContainer);
}
// Add unknown reagents (if any)
if (unknownReagents > 0)
{
var unknownReagentContainer = new BoxContainer
{
Align = AlignMode.Begin,
Orientation = LayoutOrientation.Vertical
};
unknownReagentContainer.AddChild(
new RichTextLabel
{
Text = Loc.GetString("health-analyzer-plus-window-unknown-reagents-text",
("amount", Math.Round(unknownReagents, 1).ToString("0.0"))
)
}
);
BloodstreamReagentListContainer.AddChild(unknownReagentContainer);
}
return;
}
// End DeltaV - Health Analyzer Plus
private Texture GetTexture(string texture) private Texture GetTexture(string texture)
{ {
var rsiPath = new ResPath("/Textures/Objects/Devices/health_analyzer.rsi"); var rsiPath = new ResPath("/Textures/Objects/Devices/health_analyzer.rsi");
@@ -7,9 +7,7 @@
<ScrollContainer <ScrollContainer
Margin="5 5 5 5" Margin="5 5 5 5"
ReturnMeasure="True" ReturnMeasure="True"
VerticalExpand="True" VerticalExpand="True">
VScrollBarHidden="True"
HScrollBarHidden="True"> <!-- DeltaV - Hide the scroll bars -->
<ui:HealthAnalyzerControl <ui:HealthAnalyzerControl
Name="HealthAnalyzer" Name="HealthAnalyzer"
-3
View File
@@ -34,9 +34,6 @@ namespace Content.Client.Input
common.AddFunction(ContentKeyFunctions.RotateStoredItem); common.AddFunction(ContentKeyFunctions.RotateStoredItem);
common.AddFunction(ContentKeyFunctions.SaveItemLocation); common.AddFunction(ContentKeyFunctions.SaveItemLocation);
common.AddFunction(ContentKeyFunctions.Point); common.AddFunction(ContentKeyFunctions.Point);
// Floofstation section
common.AddFunction(ContentKeyFunctions.OfferItem);
// Floofstation section end
common.AddFunction(ContentKeyFunctions.ZoomOut); common.AddFunction(ContentKeyFunctions.ZoomOut);
common.AddFunction(ContentKeyFunctions.ZoomIn); common.AddFunction(ContentKeyFunctions.ZoomIn);
common.AddFunction(ContentKeyFunctions.ResetZoom); common.AddFunction(ContentKeyFunctions.ResetZoom);
@@ -32,10 +32,10 @@ namespace Content.Client.Labels.UI
} }
_window.OnLabelChanged += OnLabelChanged; _window.OnLabelChanged += OnLabelChanged;
_window.OnLabelSelected += OnLabelSelected; // Starlight
Reload(); Reload();
_window.SetInitialLabelState(); // Must be after Reload() has set the label text _window.SetInitialLabelState(); // Must be after Reload() has set the label text
} }
private void OnLabelChanged(string newLabel) private void OnLabelChanged(string newLabel)
{ {
// Focus moment // Focus moment
@@ -45,17 +45,7 @@ namespace Content.Client.Labels.UI
SendPredictedMessage(new HandLabelerLabelChangedMessage(newLabel)); 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() public void Reload()
{ {
if (_window == null || !_entManager.TryGetComponent(Owner, out HandLabelerComponent? component)) if (_window == null || !_entManager.TryGetComponent(Owner, out HandLabelerComponent? component))
@@ -1,10 +1,8 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 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'}" Title="{Loc 'hand-labeler-ui-header'}"
SetWidth="400" 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"> <BoxContainer Orientation="Vertical" SeparationOverride="4">
<Label Name="CurrentTextLabel" Text="{Loc 'hand-labeler-current-text-label'}" /> <Label Name="CurrentTextLabel" Text="{Loc 'hand-labeler-current-text-label'}" />
<LineEdit Name="LabelLineEdit" /> <LineEdit Name="LabelLineEdit" />
@@ -12,19 +10,5 @@
<Button Name="ResetLabelButton" Text="{Loc 'hand-labeler-ui-reset-label-text'}" StyleClasses="OpenRight" /> <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" /> <Button Name="ClearLabelButton" Text="{Loc 'hand-labeler-ui-clear-label-text'}" StyleClasses="OpenLeft" />
</BoxContainer> </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> </BoxContainer>
</DefaultWindow> </DefaultWindow>
@@ -1,19 +1,13 @@
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML; 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 namespace Content.Client.Labels.UI
{ {
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class HandLabelerWindow : DefaultWindow public sealed partial class HandLabelerWindow : DefaultWindow
{ {
[Dependency] private readonly IConfigurationManager _cfg = default!; // Starlight
public event Action<string>? OnLabelChanged; public event Action<string>? OnLabelChanged;
public event Action<string>? OnLabelSelected; // Starlight
/// <summary> /// <summary>
/// Is the user currently entering text into the control? /// Is the user currently entering text into the control?
@@ -23,14 +17,10 @@ namespace Content.Client.Labels.UI
private string _label = string.Empty; private string _label = string.Empty;
private string _initialLabel = string.Empty; private string _initialLabel = string.Empty;
private bool _deleteMode = false; // Starlight
private List<string> _savedLabels = []; // Starlight
public HandLabelerWindow() public HandLabelerWindow()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this); // Starlight
LoadSavedLabelsFromCVar(); // Starlight
LabelLineEdit.OnTextChanged += e => LabelLineEdit.OnTextChanged += e =>
{ {
@@ -47,42 +37,6 @@ namespace Content.Client.Labels.UI
}; };
ResetLabelButton.OnPressed += _ => LabelLineEdit.SetText(_initialLabel, true); ResetLabelButton.OnPressed += _ => LabelLineEdit.SetText(_initialLabel, true);
ClearLabelButton.OnPressed += _ => LabelLineEdit.SetText("", 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() protected override void Opened()
@@ -116,68 +70,15 @@ namespace Content.Client.Labels.UI
UpdateButtons(); 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() public void UpdateButtons()
{ {
ResetLabelButton.Disabled = (LabelLineEdit.Text == _initialLabel); ResetLabelButton.Disabled = (LabelLineEdit.Text == _initialLabel);
ClearLabelButton.Disabled = (LabelLineEdit.Text == ""); 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) public void SetMaxLabelLength(int maxLength)
{ {
LabelLineEdit.IsValid = s => s.Length <= 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,8 +9,6 @@ using Content.Shared.Preferences;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Content.Client._CD.Records.UI; // DeltaV
using Content.Shared._DV.Body.Systems; // DeltaV
namespace Content.Client.Lobby.UI; namespace Content.Client.Lobby.UI;
@@ -195,7 +193,6 @@ public sealed partial class HumanoidProfileEditor
RefreshLoadouts(); RefreshLoadouts();
UpdateSexControls(); // update sex for new species UpdateSexControls(); // update sex for new species
UpdateSpeciesGuidebookIcon(); UpdateSpeciesGuidebookIcon();
UpdateHeightControls(); // DeltaV
ReloadPreview(); ReloadPreview();
} }
@@ -207,22 +204,23 @@ public sealed partial class HumanoidProfileEditor
ReloadProfilePreview(); ReloadProfilePreview();
} }
private void UpdateHeightControls() private void UpdateHeightControls()
{ {
if (Profile == null) if (Profile == null)
{
return; return;
}
var prototype = _prototypeManager.Index(Profile.Species); var species = _species.Find(x => x.ID == Profile.Species);
_defaultHeight = prototype.DefaultHeight; if (species != null)
_defaultHeight = species.DefaultHeight;
var prototype = _prototypeManager.Index<SpeciesPrototype>(Profile.Species);
var sliderPercent = (Profile.Height - prototype.MinHeight) / var sliderPercent = (Profile.Height - prototype.MinHeight) /
(prototype.MaxHeight - prototype.MinHeight); (prototype.MaxHeight - prototype.MinHeight);
CDHeightSlider.Value = sliderPercent; 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 // End CD - Character Records
@@ -73,13 +73,9 @@
<BoxContainer HorizontalExpand="True"> <BoxContainer HorizontalExpand="True">
<Label Text="{Loc 'humanoid-profile-editor-height-label'}" /> <Label Text="{Loc 'humanoid-profile-editor-height-label'}" />
<Control HorizontalExpand="True" /> <Control HorizontalExpand="True" />
<Label Name="CDHeightLabel" />
<Slider Name="CDHeightSlider" HorizontalAlignment="Right" SetWidth="300" MinValue="0.0" MaxValue="1.0"/> <Slider Name="CDHeightSlider" HorizontalAlignment="Right" SetWidth="300" MinValue="0.0" MaxValue="1.0"/>
</BoxContainer> <LineEdit HorizontalAlignment="Right" Name="CDHeight" MinSize="60 0" Text="1.0" />
<BoxContainer HorizontalExpand="True"> <Button Name="CDHeightReset" Text="{Loc 'humanoid-profile-editor-reset-height-button'}" HorizontalAlignment="Right"/>
<Label Text="{Loc 'humanoid-profile-editor-height-pull-speed-penalty-label'}" />
<Control HorizontalExpand="True" />
<Label Name="CDPullSpeedReductionLabel" />
</BoxContainer> </BoxContainer>
<!-- End CD - Character Records --> <!-- End CD - Character Records -->
<!-- Sex --> <!-- Sex -->
@@ -2,6 +2,9 @@ using Content.Client.Humanoid;
using Content.Client.Message; using Content.Client.Message;
using Content.Client.Players.PlayTimeTracking; using Content.Client.Players.PlayTimeTracking;
using Content.Client.Sprite; 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.CCVar;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
@@ -19,8 +22,13 @@ using Robust.Shared.ContentPack;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Direction = Robust.Shared.Maths.Direction; using Direction = Robust.Shared.Maths.Direction;
using Content.Client._CD.Records.UI; // CD - Character Records // Begin CD - Character Records
using Content.Shared._DV.Body.Systems; // DV - Traits 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
namespace Content.Client.Lobby.UI namespace Content.Client.Lobby.UI
{ {
@@ -213,18 +221,34 @@ namespace Content.Client.Lobby.UI
// Begin CD - Character Records // Begin CD - Character Records
#region CDHeight #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 += _ => CDHeightSlider.OnValueChanged += _ =>
{ {
if (Profile is null) if (Profile is null)
return; 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); 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); SetProfileHeight(newHeight);
}; };
@@ -75,11 +75,9 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
if (!LocalizedNames.TryGetValue(netEntity, out var name)) if (!LocalizedNames.TryGetValue(netEntity, out var name))
name = Loc.GetString("navmap-unknown-entity"); name = Loc.GetString("navmap-unknown-entity");
var pos = _xform.ToMapCoordinates(blip.Coordinates); // DeltaV - map-coordinates
var message = name + "\n" + Loc.GetString("navmap-location", var message = name + "\n" + Loc.GetString("navmap-location",
("x", MathF.Round(pos.X)), // DeltaV - map-coordinates ("x", MathF.Round(blip.Coordinates.X)),
("y", MathF.Round(pos.Y))); // DeltaV - map-coordinates ("y", MathF.Round(blip.Coordinates.Y)));
_trackedEntityLabel.Text = message; _trackedEntityLabel.Text = message;
_trackedEntityPanel.Visible = true; _trackedEntityPanel.Visible = true;
@@ -88,9 +88,9 @@ public sealed partial class CryoPodWindow : FancyWindow
// Health analyzer // Health analyzer
var maybePatient = _entityManager.GetEntity(msg.Health.TargetEntity); var maybePatient = _entityManager.GetEntity(msg.Health.TargetEntity);
var hasPatient = msg.Health.TargetEntity.HasValue; var hasPatient = msg.Health.TargetEntity.HasValue;
// var hasDamage = hasPatient && msg.HasDamage; // DeltaV - Health Analyzer Plus var hasDamage = hasPatient && msg.HasDamage;
NoDamageText.Visible = false; //(hasPatient && !hasDamage); // DeltaV - Health Analyzer Plus NoDamageText.Visible = (hasPatient && !hasDamage);
HealthSection.Visible = hasPatient; HealthSection.Visible = hasPatient;
EjectPatientButton.Disabled = !hasPatient; EjectPatientButton.Disabled = !hasPatient;
@@ -208,7 +208,6 @@ namespace Content.Client.Options.UI.Tabs
AddButton(ContentKeyFunctions.MovePulledObject); AddButton(ContentKeyFunctions.MovePulledObject);
AddButton(ContentKeyFunctions.ReleasePulledObject); AddButton(ContentKeyFunctions.ReleasePulledObject);
AddButton(ContentKeyFunctions.Point); AddButton(ContentKeyFunctions.Point);
AddButton(ContentKeyFunctions.OfferItem); // Floofstation
AddButton(ContentKeyFunctions.RotateObjectClockwise); AddButton(ContentKeyFunctions.RotateObjectClockwise);
AddButton(ContentKeyFunctions.RotateObjectCounterclockwise); AddButton(ContentKeyFunctions.RotateObjectCounterclockwise);
AddButton(ContentKeyFunctions.FlipObject); AddButton(ContentKeyFunctions.FlipObject);
@@ -11,7 +11,6 @@ using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components; using Content.Shared.StatusIcon.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Player; // DeltaV
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using static Robust.Shared.Maths.Color; using static Robust.Shared.Maths.Color;
@@ -23,7 +22,6 @@ namespace Content.Client.Overlays;
/// </summary> /// </summary>
public sealed class EntityHealthBarOverlay : Overlay public sealed class EntityHealthBarOverlay : Overlay
{ {
[Dependency] private readonly IPlayerManager _player = default!; // DeltaV
private readonly IEntityManager _entManager; private readonly IEntityManager _entManager;
private readonly IPrototypeManager _prototype; private readonly IPrototypeManager _prototype;
@@ -42,8 +40,6 @@ public sealed class EntityHealthBarOverlay : Overlay
public EntityHealthBarOverlay(IEntityManager entManager, IPrototypeManager prototype) public EntityHealthBarOverlay(IEntityManager entManager, IPrototypeManager prototype)
{ {
IoCManager.InjectDependencies(this); // DeltaV
_entManager = entManager; _entManager = entManager;
_prototype = prototype; _prototype = prototype;
_transform = _entManager.System<SharedTransformSystem>(); _transform = _entManager.System<SharedTransformSystem>();
@@ -73,11 +69,6 @@ public sealed class EntityHealthBarOverlay : Overlay
out var damageableComponent, out var damageableComponent,
out var spriteComponent)) out var spriteComponent))
{ {
// BEGIN DeltaV - You can't see your own bar :godo:
if (_player.LocalEntity is { } player && player.Id == uid.Id)
continue;
// END DeltaV
if (statusIcon != null && !_statusIconSystem.IsVisible((uid, _entManager.GetComponent<MetaDataComponent>(uid)), statusIcon)) if (statusIcon != null && !_statusIconSystem.IsVisible((uid, _entManager.GetComponent<MetaDataComponent>(uid)), statusIcon))
continue; continue;
@@ -85,40 +85,41 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
{ {
case AsteroidOffering asteroid: case AsteroidOffering asteroid:
option.Title = Loc.GetString($"dungeon-config-proto-{asteroid.Id}"); option.Title = Loc.GetString($"dungeon-config-proto-{asteroid.Id}");
var layerKeys = asteroid.MarkerLayers.Keys.ToList(); break; // DeltaV: Skip ores since they aren't used with custom generation
layerKeys.Sort(); // var layerKeys = asteroid.MarkerLayers.Keys.ToList();
// layerKeys.Sort();
foreach (var resource in layerKeys) //
{ // foreach (var resource in layerKeys)
var count = asteroid.MarkerLayers[resource]; // {
// var count = asteroid.MarkerLayers[resource];
var container = new BoxContainer //
{ // var container = new BoxContainer
Orientation = BoxContainer.LayoutOrientation.Horizontal, // {
HorizontalExpand = true, // Orientation = BoxContainer.LayoutOrientation.Horizontal,
}; // HorizontalExpand = true,
// };
var resourceLabel = new Label //
{ // var resourceLabel = new Label
Text = Loc.GetString("salvage-magnet-resources", // {
("resource", resource)), // Text = Loc.GetString("salvage-magnet-resources",
HorizontalAlignment = Control.HAlignment.Left, // ("resource", resource)),
}; // HorizontalAlignment = Control.HAlignment.Left,
// };
var countLabel = new Label //
{ // var countLabel = new Label
Text = Loc.GetString("salvage-magnet-resources-count", ("count", count)), // {
HorizontalAlignment = Control.HAlignment.Right, // Text = Loc.GetString("salvage-magnet-resources-count", ("count", count)),
HorizontalExpand = true, // HorizontalAlignment = Control.HAlignment.Right,
}; // HorizontalExpand = true,
// };
container.AddChild(resourceLabel); //
container.AddChild(countLabel); // container.AddChild(resourceLabel);
// container.AddChild(countLabel);
option.AddContent(container); //
} // option.AddContent(container);
// }
break; //
// break;
case DebrisOffering debris: case DebrisOffering debris:
option.Title = Loc.GetString($"salvage-magnet-debris-{debris.Id}"); option.Title = Loc.GetString($"salvage-magnet-debris-{debris.Id}");
break; break;
@@ -1,8 +1,9 @@
<controls:FancyWindow xmlns="https://spacestation14.io" <controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls" xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:chassisSpriteControls="clr-namespace:Content.Client._CD.Silicons.Borgs.UI"
Title="{Loc 'borg-select-type-menu-title'}" Title="{Loc 'borg-select-type-menu-title'}"
SetSize="550 300"> SetSize="600 480">
<BoxContainer Orientation="Vertical"> <BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal" VerticalExpand="True"> <BoxContainer Orientation="Horizontal" VerticalExpand="True">
<!-- Left pane: selection of borg type --> <!-- Left pane: selection of borg type -->
@@ -29,6 +30,9 @@
<RichTextLabel Name="DescriptionLabel" VerticalExpand="True" VerticalAlignment="Top" /> <RichTextLabel Name="DescriptionLabel" VerticalExpand="True" VerticalAlignment="Top" />
</BoxContainer> </BoxContainer>
</Control> </Control>
<chassisSpriteControls:ChassisSpriteSelection Name="ChassisSpriteSelection"/> <!-- CD change -->
<controls:ConfirmButton Name="ConfirmTypeButton" Text="{Loc 'borg-select-type-menu-confirm'}" <controls:ConfirmButton Name="ConfirmTypeButton" Text="{Loc 'borg-select-type-menu-confirm'}"
Disabled="True" HorizontalAlignment="Right" Disabled="True" HorizontalAlignment="Right"
MinWidth="200" /> MinWidth="200" />
@@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Systems.Guidebook; using Content.Client.UserInterface.Systems.Guidebook;
using Content.Shared._CD.Silicons; // CosmicDrift
using Content.Shared.Guidebook; using Content.Shared.Guidebook;
using Content.Shared.Silicons.Borgs; using Content.Shared.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components; using Content.Shared.Silicons.Borgs.Components;
@@ -29,6 +30,7 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
private BorgTypePrototype? _selectedBorgType; private BorgTypePrototype? _selectedBorgType;
public event Action<ProtoId<BorgTypePrototype>>? ConfirmedBorgType; public event Action<ProtoId<BorgTypePrototype>>? ConfirmedBorgType;
public event Action<EntityPrototype?>? ConfirmedBorgSubtype; // CosmicDrift event - borg subtypes
private static readonly List<ProtoId<GuideEntryPrototype>> GuidebookEntries = new() { "Cyborgs", "Robotics" }; private static readonly List<ProtoId<GuideEntryPrototype>> GuidebookEntries = new() { "Cyborgs", "Robotics" };
@@ -56,6 +58,13 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
ConfirmTypeButton.OnPressed += ConfirmButtonPressed; ConfirmTypeButton.OnPressed += ConfirmButtonPressed;
HelpGuidebookIds = GuidebookEntries; HelpGuidebookIds = GuidebookEntries;
// Start CosmicDrift Changes - borg subtypes
ChassisSpriteSelection.SubtypeSelected += () =>
{
ConfirmTypeButton.Disabled = false;
};
// End CosmicDrift Changes - borg subtypes
} }
private void UpdateInformation(BorgTypePrototype prototype) private void UpdateInformation(BorgTypePrototype prototype)
@@ -87,6 +96,14 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
NameLabel.Text = PrototypeName(prototype); NameLabel.Text = PrototypeName(prototype);
DescriptionLabel.Text = Loc.GetString($"borg-type-{prototype.ID}-desc"); DescriptionLabel.Text = Loc.GetString($"borg-type-{prototype.ID}-desc");
ChassisView.SetPrototype(prototype.DummyPrototype); ChassisView.SetPrototype(prototype.DummyPrototype);
// Start CosmicDrift Changes - borg subtypes
if (_selectedBorgType != null)
{
ConfirmTypeButton.Disabled = true;
ChassisSpriteSelection.Update(_selectedBorgType);
}
// End CosmicDrift Changes - borg subtypes
} }
private void ConfirmButtonPressed(BaseButton.ButtonEventArgs obj) private void ConfirmButtonPressed(BaseButton.ButtonEventArgs obj)
@@ -94,6 +111,7 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
if (_selectedBorgType == null) if (_selectedBorgType == null)
return; return;
ConfirmedBorgSubtype?.Invoke(ChassisSpriteSelection.SubtypePrototype); // CosmicDrift
ConfirmedBorgType?.Invoke(_selectedBorgType); ConfirmedBorgType?.Invoke(_selectedBorgType);
} }
@@ -1,4 +1,6 @@
using Content.Shared.Silicons.Borgs.Components; using Content.Shared._CD.Silicons; // CosmicDrift
using Content.Shared._CD.Silicons.Borgs; // CosmicDrift
using Content.Shared.Silicons.Borgs.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
@@ -26,5 +28,6 @@ public sealed class BorgSelectTypeUserInterface : BoundUserInterface
_menu = this.CreateWindow<BorgSelectTypeMenu>(); _menu = this.CreateWindow<BorgSelectTypeMenu>();
_menu.ConfirmedBorgType += prototype => SendPredictedMessage(new BorgSelectTypeMessage(prototype)); _menu.ConfirmedBorgType += prototype => SendPredictedMessage(new BorgSelectTypeMessage(prototype));
_menu.ConfirmedBorgSubtype += subtypePrototype => SendPredictedMessage(new BorgSelectSubtypeMessage(subtypePrototype?.ID)); // CosmicDrift - borg subtypes
} }
} }
@@ -1,9 +1,12 @@
using Content.Client.PDA; // DeltaV using Content.Client.PDA; // DeltaV
using Content.Shared._CD.Silicons.Borgs; // CosmicDrift
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Content.Shared.Silicons.Borgs; using Content.Shared.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components; using Content.Shared.Silicons.Borgs.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.Serialization; // DeltaV using Robust.Client.ResourceManagement; // CosmicDrift
using Robust.Shared.Serialization.TypeSerializers.Implementations; // CosmicDrift
using Robust.Shared.Timing; // CosmicDrift
namespace Content.Client.Silicons.Borgs; namespace Content.Client.Silicons.Borgs;
@@ -17,6 +20,7 @@ public sealed partial class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeS
[Dependency] private readonly BorgSystem _borgSystem = default!; [Dependency] private readonly BorgSystem _borgSystem = default!;
[Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!; [Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly IGameTiming _timing = default!; // CosmicDrift - borg subtypes
public override void Initialize() public override void Initialize()
{ {
@@ -40,15 +44,20 @@ public sealed partial class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeS
Entity<BorgSwitchableTypeComponent> entity, Entity<BorgSwitchableTypeComponent> entity,
BorgTypePrototype prototype) BorgTypePrototype prototype)
{ {
// Begin DeltaV Additions // Begin Afterlight Addition - added checks to stop sprite state errors
if (prototype.ClientComponents is {} add) if (!_timing.IsFirstTimePredicted)
EntityManager.AddComponents(entity, add); return;
// End DeltaV Additions
if (TryComp<BorgSwitchableSubtypeComponent>(entity, out var subtype) &&
subtype.BorgSubtype != null)
{
var ev = new TypeTryingToUpdateVisualsEvent();
RaiseLocalEvent(entity, ref ev);
return;
}
// End Afterlight Additions - added checks to stop sprite state errors
if (TryComp(entity, out SpriteComponent? sprite)) if (TryComp(entity, out SpriteComponent? sprite))
{ {
// Begin DeltaV Additions - work around engine bug with AddComponents
((ISerializationHooks) sprite).AfterDeserialization();
// End DeltaV Additions
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.Body, prototype.SpriteBodyState); _sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.Body, prototype.SpriteBodyState);
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.LightStatus, prototype.SpriteToggleLightState); _sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.LightStatus, prototype.SpriteToggleLightState);
} }
@@ -76,6 +85,26 @@ public sealed partial class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeS
} }
// DeltaV - borg pdas // DeltaV - borg pdas
// Start CosmicDrift Changes - borg subtypes
if (prototype.SpriteBodyMovementState is { } movementState)
{
var spriteMovement = EnsureComp<SpriteMovementComponent>(entity);
spriteMovement.NoMovementLayers.Clear();
spriteMovement.NoMovementLayers["movement"] = new PrototypeLayerData
{
State = prototype.SpriteBodyState,
};
spriteMovement.MovementLayers.Clear();
spriteMovement.MovementLayers["movement"] = new PrototypeLayerData
{
State = movementState,
};
}
else
{
RemComp<SpriteMovementComponent>(entity);
}
// End CosmicDrift Changes - borg subtypes
base.UpdateEntityAppearance(entity, prototype); base.UpdateEntityAppearance(entity, prototype);
} }
} }
@@ -70,11 +70,6 @@ public sealed class ConfirmButton : Button
[ViewVariables] [ViewVariables]
public bool IsConfirming = false; public bool IsConfirming = false;
// Begin DeltaV
[ViewVariables]
public bool ForceDisabled = false;
// End DeltaV
public ConfirmButton() public ConfirmButton()
{ {
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
@@ -92,7 +87,7 @@ public sealed class ConfirmButton : Button
} }
if (Disabled && _gameTiming.CurTime > _nextCooldown) if (Disabled && _gameTiming.CurTime > _nextCooldown)
Disabled = ForceDisabled; // DeltaV Disabled = false;
} }
protected override void DrawModeChanged() protected override void DrawModeChanged()
@@ -49,6 +49,11 @@
<Control HorizontalExpand="True" /> <Control HorizontalExpand="True" />
<Label Name="RecordContainerSpecies" Align="Right" /> <Label Name="RecordContainerSpecies" Align="Right" />
</BoxContainer> </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"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc 'humanoid-profile-editor-cd-records-weight'}"/> <Label Text="{Loc 'humanoid-profile-editor-cd-records-weight'}"/>
<Control HorizontalExpand="True" /> <Control HorizontalExpand="True" />
@@ -325,7 +325,8 @@ public sealed partial class CharacterRecordViewer : FancyWindow
RecordContainerJob.Text = record.JobTitle; /* At some point in the future we might want to display the icon */ RecordContainerJob.Text = record.JobTitle; /* At some point in the future we might want to display the icon */
RecordContainerGender.Text = record.Gender.ToString(); RecordContainerGender.Text = record.Gender.ToString();
RecordContainerSpecies.Text = record.Species; RecordContainerSpecies.Text = record.Species;
RecordContainerWeight.Text = $"{cr.Weight} ({UnitConversion.GetImperialDisplayMass(cr.Weight)})"; RecordContainerHeight.Text = cr.Height + " " + UnitConversion.GetImperialDisplayLength(cr.Height);
RecordContainerWeight.Text = cr.Weight + " " + UnitConversion.GetImperialDisplayMass(cr.Weight);
RecordContainerContactName.SetValue(cr.EmergencyContactName); RecordContainerContactName.SetValue(cr.EmergencyContactName);
RecordContainerEmployment.Visible = false; RecordContainerEmployment.Visible = false;
@@ -5,6 +5,12 @@
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="10"> <BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="10">
<!-- Height, Weight --> <!-- Height, Weight -->
<GridContainer Columns="2"> <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"> <BoxContainer HorizontalExpand="True" SeparationOverride="2">
<Label Text="{Loc 'humanoid-profile-editor-cd-records-weight'}" /> <Label Text="{Loc 'humanoid-profile-editor-cd-records-weight'}" />
<Control HorizontalExpand="True" MinSize="5 0" /> <Control HorizontalExpand="True" MinSize="5 0" />
@@ -25,6 +25,14 @@ public sealed partial class RecordEditorGui : Control
#region General #region General
HeightEdit.OnTextChanged += args =>
{
if (!int.TryParse(args.Text, out var newHeight))
return;
UpdateImperialHeight(newHeight);
UpdateRecords(_records.WithHeight(newHeight));
};
WeightEdit.OnTextChanged += args => WeightEdit.OnTextChanged += args =>
{ {
if (!int.TryParse(args.Text, out var newWeight)) if (!int.TryParse(args.Text, out var newWeight))
@@ -120,6 +128,8 @@ public sealed partial class RecordEditorGui : Control
private void UpdateWidgets() private void UpdateWidgets()
{ {
HeightEdit.SetText(_records.Height.ToString());
UpdateImperialHeight(_records.Height);
WeightEdit.SetText(_records.Weight.ToString()); WeightEdit.SetText(_records.Weight.ToString());
UpdateImperialWeight(_records.Weight); UpdateImperialWeight(_records.Weight);
ContactNameEdit.SetText(_records.EmergencyContactName); ContactNameEdit.SetText(_records.EmergencyContactName);
@@ -133,8 +143,13 @@ public sealed partial class RecordEditorGui : Control
PostmortemEdit.SetText(_records.PostmortemInstructions); PostmortemEdit.SetText(_records.PostmortemInstructions);
} }
private void UpdateImperialHeight(int newHeight)
{
HeightImperialLabel.Text = UnitConversion.GetImperialDisplayLength(newHeight);
}
private void UpdateImperialWeight(int newWeight) private void UpdateImperialWeight(int newWeight)
{ {
WeightImperialLabel.Text = $"({UnitConversion.GetImperialDisplayMass(newWeight)})"; WeightImperialLabel.Text = UnitConversion.GetImperialDisplayMass(newWeight);
} }
} }
@@ -2,47 +2,15 @@ namespace Content.Client._CD.Records.UI;
public static class UnitConversion 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) public static string GetImperialDisplayLength(int lengthCm)
{ {
var heightIn = (int)Math.Round(lengthCm * 0.3937007874 /* cm to in*/); var heightIn = (int) Math.Round(lengthCm * 0.3937007874 /* cm to in*/);
return $"{heightIn / 12}'{heightIn % 12}\""; return $"({heightIn / 12}'{heightIn % 12}'')";
} }
public static string GetImperialDisplayMass(int massKg) public static string GetImperialDisplayMass(int massKg)
{ {
var weightLbs = (int)Math.Round(massKg * 2.2046226218 /* kg to lbs */); var weightLbs = (int) Math.Round(massKg * 2.2046226218 /* kg to lbs */);
return $"{weightLbs} lbs"; return $"({weightLbs} lbs)";
} }
} }
@@ -0,0 +1,102 @@
using System.Linq;
using Content.Client.Silicons.Borgs;
using Content.Shared._CD.Silicons.Borgs;
using Content.Shared.Movement.Components;
using Content.Shared.Silicons.Borgs.Components;
using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Client._CD.Silicons.Borgs;
/// <summary>
/// Primarily handles the appearance aspects of the borg subtype.
/// </summary>
public sealed class BorgSwitchableSubtypeSystem : SharedBorgSwitchableSubtypeSystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly BorgSystem _borg = default!;
[Dependency] private readonly AppearanceSystem _appearance = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BorgSwitchableSubtypeComponent, ComponentStartup>(OnComponentStartup);
SubscribeLocalEvent<BorgSwitchableSubtypeComponent, AfterAutoHandleStateEvent>(OnAutoHandleEvent);
}
private void OnAutoHandleEvent(Entity<BorgSwitchableSubtypeComponent> ent, ref AfterAutoHandleStateEvent args)
{
SelectBorgSubtype(ent);
}
private void OnComponentStartup(Entity<BorgSwitchableSubtypeComponent> ent, ref ComponentStartup args)
{
SelectBorgSubtype(ent);
}
protected override void UpdateEntityAppearance(Entity<BorgSwitchableSubtypeComponent> entity, EntityPrototype borgSubtypePrototype)
{
// LOT of copy pasted code from BorgSwitchableTypeSystem, but is probably necessary unless the upstream code
// is refactored
if (!borgSubtypePrototype.TryGetComponent<BorgSubtypeDefinitionComponent>(out var borgSubtype, ComponentFactory))
return;
// get our required components
var (owner, _) = entity;
if (!TryComp<SpriteComponent>(entity, out var chassisSprite))
return;
// remove all existing layers
for (int i = chassisSprite.AllLayers.Count() - 1; i >= 0; i--)
{
_sprite.RemoveLayer((entity, chassisSprite), i);
}
for (int i = 0; i < borgSubtype.LayerData.Length; i++)
{
var layerData = borgSubtype.LayerData[i];
layerData.RsiPath = borgSubtype.SpritePath?.ToString();
if (borgSubtype.Offset != null)
layerData.Offset = borgSubtype.Offset;
_sprite.AddLayer((owner, chassisSprite), layerData, i);
}
if (TryComp<BorgChassisComponent>(entity, out var chassis))
{
_borg.SetMindStates(
(entity.Owner, chassis),
borgSubtype.SpriteHasMindState,
borgSubtype.SpriteNoMindState);
if (TryComp(entity, out AppearanceComponent? appearance))
{
// Queue update so state changes apply.
_appearance.QueueUpdate(entity, appearance);
}
}
if (borgSubtype.SpriteBodyMovementState is { } movementState)
{
var spriteMovement = EnsureComp<SpriteMovementComponent>(entity);
spriteMovement.NoMovementLayers.Clear();
spriteMovement.NoMovementLayers["movement"] = new PrototypeLayerData
{
State = borgSubtype.SpriteBodyState,
};
spriteMovement.MovementLayers.Clear();
spriteMovement.MovementLayers["movement"] = new PrototypeLayerData
{
State = movementState,
};
}
else
{
RemComp<SpriteMovementComponent>(entity);
}
base.UpdateEntityAppearance(entity, borgSubtypePrototype);
}
}
@@ -0,0 +1,23 @@
<Control xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
<BoxContainer Name="MainContainer"
Orientation="Vertical"
SeparationOverride="4"
Visible="False">
<controls:StripeBack>
<Label Text="{Loc 'cd-borg-select-subtype-flavour-text'}"
StyleClasses="LabelSubText"
HorizontalAlignment="Center"
Margin="0 12"/>
</controls:StripeBack>
<ScrollContainer Name="OptionsScrollContainer"
HScrollEnabled="True"
VScrollEnabled="False"
MinHeight="96">
<BoxContainer Name="OptionsContainer"
Orientation="Horizontal"
SeparationOverride="4"/>
</ScrollContainer>
</BoxContainer>
</Control>
@@ -0,0 +1,99 @@
using Content.Shared.Silicons.Borgs;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client._CD.Silicons.Borgs.UI;
[GenerateTypedNameReferences]
public sealed partial class ChassisSpriteSelection : Control
{
[Dependency] private readonly IComponentFactory _componentFactory = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
public EntityPrototype? SubtypePrototype;
public event Action? SubtypeSelected;
private const int PrototypeViewSize = 2;
private static readonly ProtoId<EntityCategoryPrototype> _borgSubtypeCategory = "BorgSubtype";
public ChassisSpriteSelection()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}
public void Update(BorgTypePrototype borgTypePrototype)
{
MainContainer.Visible = true;
OptionsContainer.RemoveAllChildren();
var buttonGroup = new ButtonGroup();
List<Button> buttons = new List<Button>();
buttons.Add(CreateDefaultSubtypeButton(borgTypePrototype, buttonGroup));
foreach (var ent in _prototype.Categories.GetValueRefOrNullRef(_borgSubtypeCategory))
{
if (!ent.TryGetComponent<Shared._CD.Silicons.Borgs.BorgSubtypeDefinitionComponent>(out var subtype, _componentFactory))
continue;
// Only add subtypes of the current selected 'main' borg type (engineering, medical, etc.)
if (subtype.ParentType != borgTypePrototype.ID)
continue;
var button = new Button
{
ToolTip = Loc.GetString($"al-borg-{borgTypePrototype.ID}-subtype-{ent.Name.Replace(' ', '-').ToLower()}-name"),
Group = buttonGroup,
MinHeight = 32,
};
button.OnPressed += _ =>
{
SubtypePrototype = ent;
SubtypeSelected?.Invoke();
};
button.AddChild(CreateEntityPrototypeView(subtype.DummyPrototype));
buttons.Add(button);
}
foreach (var button in buttons)
{
OptionsContainer.AddChild(button);
}
}
private Button CreateDefaultSubtypeButton(BorgTypePrototype borgTypePrototype, ButtonGroup group)
{
var button = new Button
{
ToolTip = "default",
Group = group,
MinHeight = 32,
};
button.OnPressed += _ =>
{
SubtypePrototype = null;
SubtypeSelected?.Invoke();
};
button.AddChild(CreateEntityPrototypeView(borgTypePrototype.DummyPrototype));
return button;
}
private EntityPrototypeView CreateEntityPrototypeView(EntProtoId entProtoId)
{
var entPrototypeView = new EntityPrototypeView();
entPrototypeView.SetPrototype(entProtoId);
entPrototypeView.Scale *= PrototypeViewSize;
return entPrototypeView;
}
}
@@ -1,30 +0,0 @@
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);
}
}
@@ -1,117 +0,0 @@
<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>
@@ -1,320 +0,0 @@
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));
}
}
@@ -1,26 +0,0 @@
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);
}
}
@@ -1,73 +0,0 @@
using System.Linq;
using Content.Shared._DV.Forensics;
using Robust.Client.GameObjects;
using Robust.Client.ResourceManagement;
using Robust.Shared.Containers;
using Robust.Shared.Utility;
namespace Content.Client._DV.Forensics;
public sealed class DVItemSlotVisualsSystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
private static readonly ResPath TextureRoot = new("/Textures");
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DVItemSlotVisualsComponent, EntInsertedIntoContainerMessage>(OnInsertedIntoContainer);
SubscribeLocalEvent<DVItemSlotVisualsComponent, EntRemovedFromContainerMessage>(OnRemovedFromContainer);
}
private void RefreshVisuals(Entity<DVItemSlotVisualsComponent> ent)
{
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
if (slot.ContainedEntity is { } contained)
{
_sprite.CopySprite(contained, ent.Owner);
_sprite.AddLayer(ent.Owner, ent.Comp.FilledSprite);
}
else
{
var sprite = Comp<SpriteComponent>(ent);
var count = sprite.AllLayers.Count();
for (var i = count - 1; i >= 0; i--)
{
_sprite.RemoveLayer((ent, sprite), i);
}
if (ent.Comp.UnfilledSprite is not SpriteSpecifier.Rsi rsi)
{
throw new InvalidOperationException($"{ToPrettyString(ent)} has an unfilled sprite that's not an RSI");
}
if (!_resourceCache.TryGetResource<RSIResource>(TextureRoot / rsi.RsiPath, out var res))
{
throw new InvalidOperationException($"{ToPrettyString(ent)} has invalid RSI: {TextureRoot / rsi.RsiPath}");
}
_sprite.SetBaseRsi(ent.Owner, res.RSI);
_sprite.AddLayer(ent.Owner, ent.Comp.UnfilledSprite);
}
}
private void OnInsertedIntoContainer(Entity<DVItemSlotVisualsComponent> ent, ref EntInsertedIntoContainerMessage args)
{
if (args.Container.ID != ent.Comp.ItemSlot)
return;
RefreshVisuals(ent);
}
private void OnRemovedFromContainer(Entity<DVItemSlotVisualsComponent> ent, ref EntRemovedFromContainerMessage args)
{
if (args.Container.ID != ent.Comp.ItemSlot)
return;
RefreshVisuals(ent);
}
}
@@ -1,5 +0,0 @@
using Content.Shared._DV.KeycardAuthenticationDevice;
namespace Content.Client._DV.KeycardAuthenticationDevice;
public sealed class DVStationKeycardAuthenticationDeviceSystem : SharedDVStationKeycardAuthenticationDeviceSystem;
@@ -1,104 +0,0 @@
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();
}
}
}
@@ -1,56 +0,0 @@
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);
}
}
@@ -1,103 +0,0 @@
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);
}
}
@@ -1,140 +0,0 @@
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);
}
}
}
@@ -1,43 +0,0 @@
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;
}
@@ -1,73 +0,0 @@
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,5 @@
using Content.Shared._DV.Silicons;
namespace Content.Client._DV.Silicons;
public sealed class SiliconEmpSystem : SharedSiliconEmpSystem;
@@ -1,23 +0,0 @@
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,71 +0,0 @@
using System.Numerics;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Shared.Enums;
using Robust.Shared.Utility;
namespace Content.Client._Floof.OfferItem;
public sealed class OfferItemIndicatorsOverlay : Overlay
{
private readonly IInputManager _inputManager;
private readonly IEntityManager _entMan;
private readonly IEyeManager _eye;
private readonly OfferItemSystem _offer;
private readonly Texture _sight;
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
private readonly Color _mainColor = Color.White.WithAlpha(0.3f);
private readonly Color _strokeColor = Color.Black.WithAlpha(0.5f);
private readonly float _scale = 0.6f; // 1 is a little big
public OfferItemIndicatorsOverlay(IInputManager input, IEntityManager entMan,
IEyeManager eye, OfferItemSystem offerSys)
{
_inputManager = input;
_entMan = entMan;
_eye = eye;
_offer = offerSys;
var spriteSys = _entMan.EntitySysManager.GetEntitySystem<SpriteSystem>();
_sight = spriteSys.Frame0(new SpriteSpecifier.Rsi(new("/Textures/_Floof/Interface/Misc/give_item.rsi"), "give_item"));
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (!_offer.IsInOfferMode())
return false;
return base.BeforeDraw(in args);
}
protected override void Draw(in OverlayDrawArgs args)
{
var mouseScreenPosition = _inputManager.MouseScreenPosition;
var mousePosMap = _eye.PixelToMap(mouseScreenPosition);
if (mousePosMap.MapId != args.MapId)
return;
var mousePos = mouseScreenPosition.Position;
var uiScale = (args.ViewportControl as Control)?.UIScale ?? 1f;
var limitedScale = uiScale > 1.25f ? 1.25f : uiScale;
DrawSight(_sight, args.ScreenHandle, mousePos, limitedScale * _scale);
}
private void DrawSight(Texture sight, DrawingHandleScreen screen, Vector2 centerPos, float scale)
{
var sightSize = sight.Size * scale;
var expandedSize = sightSize + new Vector2(7f, 7f);
screen.DrawTextureRect(sight,
UIBox2.FromDimensions(centerPos - sightSize * 0.5f, sightSize), _strokeColor);
screen.DrawTextureRect(sight,
UIBox2.FromDimensions(centerPos - expandedSize * 0.5f, expandedSize), _mainColor);
}
}
@@ -1,39 +0,0 @@
using Content.Shared._Floof.OfferItem;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
namespace Content.Client._Floof.OfferItem;
public sealed class OfferItemSystem : SharedOfferItemSystem
{
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IEyeManager _eye = default!;
public override void Initialize()
{
base.Initialize();
_overlayManager.AddOverlay(new OfferItemIndicatorsOverlay(
_inputManager,
EntityManager,
_eye,
this));
}
public override void Shutdown()
{
_overlayManager.RemoveOverlay<OfferItemIndicatorsOverlay>();
base.Shutdown();
}
public bool IsInOfferMode()
{
var entity = _playerManager.LocalEntity;
if (entity == null)
return false;
return IsInOfferMode(entity.Value);
}
}
@@ -1,24 +0,0 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'content-warning-title'}"
MinSize="700 750"
MaxSize="750 800"> <!-- DeltaV - increased maximum size -->
<BoxContainer Orientation="Vertical" Margin="20">
<Label Name="TitleLabel"
StyleClasses="LabelBig"
HorizontalAlignment="Center"
Margin="0 0 0 5"
Text="{Loc 'content-warning-label'}"/>
<PanelContainer Margin="0 0 0 10">
<RichTextLabel Name="ContentWarningBody" HorizontalAlignment="Center"/>
</PanelContainer>
<BoxContainer Orientation="Vertical" VerticalAlignment="Bottom" Margin="0 10 0 0">
<Button Name="ContentWarningAccept"
MinWidth="75"
Text="{ Loc 'content-warning-accept' }"/>
<Button Name="ContentWarningReject"
MinWidth="75"
Text="{ Loc 'content-warning-reject' }"/>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>
@@ -1,40 +0,0 @@
using Content.Client.UserInterface.Controls;
using Robust.Client.AutoGenerated;
using Robust.Client.Console;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client._Funkystation.ContentWarning;
[GenerateTypedNameReferences]
public sealed partial class ContentWarningPopup : FancyWindow
{
public event Action? OnContentWarningAccept;
public event Action? OnContentWarningReject;
public ContentWarningPopup()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
InitWindow();
}
private void InitWindow()
{
ContentWarningBody.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("content-warning-message")));
ContentWarningReject.OnPressed += _ =>
{
OnContentWarningReject?.Invoke();
};
ContentWarningAccept.OnPressed += obj =>
{
OnContentWarningAccept?.Invoke();
};
}
}
@@ -1,60 +0,0 @@
using Content.Client.Gameplay;
using Content.Client.Lobby;
using Content.Shared._Funkystation.CCVars;
using JetBrains.Annotations;
using Robust.Client.Console;
using Robust.Client.UserInterface.Controllers;
using Robust.Shared.Configuration;
namespace Content.Client._Funkystation.ContentWarning;
[UsedImplicitly]
public sealed class ContentWarningUIController : UIController, IOnStateEntered<LobbyState>, IOnStateEntered<GameplayState>
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
private ContentWarningPopup? _window;
private void AttemptOpenContentWarningPopup()
{
if (!_cfg.GetCVar(CCVars_Funky.ContentWarningDisplay) || _cfg.GetCVar(CCVars_Funky.ContentWarningAcknowledged))
return;
OpenContentWarningPopup();
}
public void OnStateEntered(LobbyState _)
{
AttemptOpenContentWarningPopup();
}
public void OnStateEntered(GameplayState _)
{
AttemptOpenContentWarningPopup();
}
private void OpenContentWarningPopup()
{
if (_window != null)
return;
_window = new ContentWarningPopup();
_window.OpenCentered();
_window.OnContentWarningReject += () =>
{
_window.Close();
_window = null;
if (_cfg.GetCVar(CCVars_Funky.ContentWarningKickOnIgnore))
_consoleHost.ExecuteCommand("quit");
};
_window.OnContentWarningAccept += () =>
{
_window.Close();
_window = null;
_cfg.SetCVar(CCVars_Funky.ContentWarningAcknowledged, true);
_cfg.SaveToFile();
};
}
}
@@ -1,5 +1,4 @@
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Shared._DV.Administration.Components; // DeltaV - Unorbitable
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -7,7 +6,7 @@ using Robust.Shared.Utility;
namespace Content.Server.Administration.Commands; namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Stealth)] [AdminCommand(AdminFlags.Stealth)]
public sealed class StealthminCommand : LocalizedEntityCommands // DeltaV - Unorbitable, LocalizedCommands to LocalizedEntityCommands public sealed class StealthminCommand : LocalizedCommands
{ {
[Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!;
@@ -27,20 +26,8 @@ public sealed class StealthminCommand : LocalizedEntityCommands // DeltaV - Unor
DebugTools.AssertNotNull(adminData); DebugTools.AssertNotNull(adminData);
if (!adminData!.Stealth) if (!adminData!.Stealth)
{
_adminManager.Stealth(player); _adminManager.Stealth(player);
// DeltaV - Unorbitable START
if (player.AttachedEntity is { } attachedEntity)
EntityManager.EnsureComponent<UnorbitableComponent>(attachedEntity);
// DeltaV END
}
else else
{
_adminManager.UnStealth(player); _adminManager.UnStealth(player);
// DeltaV - Unorbitable START
if (player.AttachedEntity is { } attachedEntity)
EntityManager.RemoveComponent<UnorbitableComponent>(attachedEntity);
// DeltaV END
}
} }
} }
@@ -1,6 +1,5 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Content.Server.Access.Systems; // DeltaV
using Content.Server.Cargo.Components; using Content.Server.Cargo.Components;
using Content.Server.NameIdentifier; using Content.Server.NameIdentifier;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
@@ -21,7 +20,6 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Content.Shared._DV.Cargo.Components; // DeltaV: Bounty claim messages
namespace Content.Server.Cargo.Systems; namespace Content.Server.Cargo.Systems;
@@ -30,7 +28,6 @@ public sealed partial class CargoSystem
[Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly NameIdentifierSystem _nameIdentifier = default!; [Dependency] private readonly NameIdentifierSystem _nameIdentifier = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSys = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSys = default!;
[Dependency] private readonly IdCardSystem _idCard = default!; // DeltaV
private static readonly ProtoId<NameIdentifierGroupPrototype> BountyNameIdentifierGroup = "Bounty"; private static readonly ProtoId<NameIdentifierGroupPrototype> BountyNameIdentifierGroup = "Bounty";
@@ -43,8 +40,6 @@ public sealed partial class CargoSystem
SubscribeLocalEvent<CargoBountyConsoleComponent, BoundUIOpenedEvent>(OnBountyConsoleOpened); SubscribeLocalEvent<CargoBountyConsoleComponent, BoundUIOpenedEvent>(OnBountyConsoleOpened);
SubscribeLocalEvent<CargoBountyConsoleComponent, BountyPrintLabelMessage>(OnPrintLabelMessage); SubscribeLocalEvent<CargoBountyConsoleComponent, BountyPrintLabelMessage>(OnPrintLabelMessage);
SubscribeLocalEvent<CargoBountyConsoleComponent, BountySkipMessage>(OnSkipBountyMessage); SubscribeLocalEvent<CargoBountyConsoleComponent, BountySkipMessage>(OnSkipBountyMessage);
SubscribeLocalEvent<CargoBountyConsoleComponent, BountyClaimedMessage>(OnBountyClaimedMessage); // DeltaV
SubscribeLocalEvent<CargoBountyConsoleComponent, BountySetStatusMessage>(OnSetBountyStatusMessage); // DeltaV
SubscribeLocalEvent<CargoBountyLabelComponent, PriceCalculationEvent>(OnGetBountyPrice); SubscribeLocalEvent<CargoBountyLabelComponent, PriceCalculationEvent>(OnGetBountyPrice);
SubscribeLocalEvent<EntitySoldEvent>(OnSold); SubscribeLocalEvent<EntitySoldEvent>(OnSold);
SubscribeLocalEvent<StationCargoBountyDatabaseComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<StationCargoBountyDatabaseComponent, MapInitEvent>(OnMapInit);
@@ -116,72 +111,6 @@ public sealed partial class CargoSystem
_audio.PlayPvs(component.SkipSound, uid); _audio.PlayPvs(component.SkipSound, uid);
} }
// Begin DeltaV bounty claim system
private void OnSetBountyStatusMessage(Entity<CargoBountyConsoleComponent> ent, ref BountySetStatusMessage args)
{
if (_station.GetOwningStation(ent.Owner) is not { } station || !TryComp<StationCargoBountyDatabaseComponent>(station, out var bountyDbComp))
return;
for (var i = 0; i < bountyDbComp.Bounties.Count; i++)
{
var bounty = bountyDbComp.Bounties[i];
if (bounty.Id != args.BountyId)
continue;
var newData = new CargoBountyData
{
Id = bounty.Id,
Bounty = bounty.Bounty,
ClaimedBy = bounty.ClaimedBy,
Status = (CargoBountyStatus)args.Status,
};
bountyDbComp.Bounties[i] = newData;
}
var untilNextSkip = bountyDbComp.NextSkipTime - Timing.CurTime;
_uiSystem.SetUiState(ent.Owner, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(bountyDbComp.Bounties, bountyDbComp.History, untilNextSkip));
}
private void OnBountyClaimedMessage(Entity<CargoBountyConsoleComponent> ent, ref BountyClaimedMessage args)
{
if (_station.GetOwningStation(ent.Owner) is not { } station || !TryComp<StationCargoBountyDatabaseComponent>(station, out var bountyDbComp))
return;
for (var i = 0; i < bountyDbComp.Bounties.Count; i++)
{
var bounty = bountyDbComp.Bounties[i];
if (bounty.Id != args.BountyId)
continue;
string name;
if (_idCard.TryFindIdCard(args.Actor, out var idCard) && idCard.Comp.FullName != null)
{
name = idCard.Comp.FullName;
}
else
{
name = Loc.GetString("bounty-console-claimed-by-unknown");
}
var newData = new CargoBountyData
{
Id = bounty.Id,
Bounty = bounty.Bounty,
ClaimedBy = name.Equals(bounty.ClaimedBy) ? string.Empty : name,
Status = bounty.Status,
};
bountyDbComp.Bounties[i] = newData;
}
var untilNextSkip = bountyDbComp.NextSkipTime - Timing.CurTime;
_uiSystem.SetUiState(ent.Owner, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(bountyDbComp.Bounties, bountyDbComp.History, untilNextSkip));
}
// End DeltaV bounty claim system
public void SetupBountyLabel(EntityUid uid, EntityUid stationId, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null) public void SetupBountyLabel(EntityUid uid, EntityUid stationId, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null)
{ {
if (!Resolve(uid, ref paper, ref label) || !_protoMan.Resolve<CargoBountyPrototype>(bounty.Bounty, out var prototype)) if (!Resolve(uid, ref paper, ref label) || !_protoMan.Resolve<CargoBountyPrototype>(bounty.Bounty, out var prototype))
@@ -1,7 +1,6 @@
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Ninja.Systems; using Content.Server.Ninja.Systems;
using Content.Shared._DV.Communications; // DeltaV Comms Console
using Content.Shared.Communications; using Content.Shared.Communications;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Interaction; using Content.Shared.Interaction;
@@ -36,7 +35,7 @@ public sealed class CommsHackerSystem : SharedCommsHackerSystem
/// </summary> /// </summary>
private void OnBeforeInteractHand(EntityUid uid, CommsHackerComponent comp, BeforeInteractHandEvent args) private void OnBeforeInteractHand(EntityUid uid, CommsHackerComponent comp, BeforeInteractHandEvent args)
{ {
if (args.Handled || !HasComp<DVCommunicationsConsoleComponent>(args.Target)) // DeltaV - Comms Console if (args.Handled || !HasComp<CommunicationsConsoleComponent>(args.Target))
return; return;
// TODO: generic check event // TODO: generic check event
@@ -23,7 +23,7 @@ using Robust.Shared.Configuration;
namespace Content.Server.Communications namespace Content.Server.Communications
{ {
public sealed class CommunicationsConsoleSystem : EntitySystem public sealed partial class CommunicationsConsoleSystem : EntitySystem // DeltaV - Partial Class
{ {
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
[Dependency] private readonly AlertLevelSystem _alertLevelSystem = default!; [Dependency] private readonly AlertLevelSystem _alertLevelSystem = default!;
@@ -53,6 +53,8 @@ namespace Content.Server.Communications
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleCallEmergencyShuttleMessage>(OnCallShuttleMessage); SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleCallEmergencyShuttleMessage>(OnCallShuttleMessage);
SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleRecallEmergencyShuttleMessage>(OnRecallShuttleMessage); SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleRecallEmergencyShuttleMessage>(OnRecallShuttleMessage);
InitializeExfiltration(); // DeltaV - Exfiltration shuttle
// On console init, set cooldown // On console init, set cooldown
SubscribeLocalEvent<CommunicationsConsoleComponent, MapInitEvent>(OnCommunicationsConsoleMapInit); SubscribeLocalEvent<CommunicationsConsoleComponent, MapInitEvent>(OnCommunicationsConsoleMapInit);
} }
@@ -136,6 +138,7 @@ namespace Content.Server.Communications
List<string>? levels = null; List<string>? levels = null;
string currentLevel = default!; string currentLevel = default!;
float currentDelay = 0; float currentDelay = 0;
TimeSpan? exfiltrationTime = null; // DeltaV - exfiltration shuttle
if (stationUid != null) if (stationUid != null)
{ {
@@ -157,6 +160,12 @@ namespace Content.Server.Communications
currentLevel = alertComp.CurrentLevel; currentLevel = alertComp.CurrentLevel;
currentDelay = _alertLevelSystem.GetAlertLevelDelay(stationUid.Value, alertComp); 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( _uiSystem.SetUiState(uid, CommunicationsConsoleUiKey.Key, new CommunicationsConsoleInterfaceState(
@@ -165,7 +174,8 @@ namespace Content.Server.Communications
levels, levels,
currentLevel, currentLevel,
currentDelay, currentDelay,
_roundEndSystem.ExpectedCountdownEnd _roundEndSystem.ExpectedCountdownEnd,
exfiltrationTime // DeltaV - exfiltration shuttle
)); ));
} }
+1
View File
@@ -1,4 +1,5 @@
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
using Content.Shared.Damage; // DeltaV - EMP damage
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Server.Radio; using Content.Server.Radio;
using Content.Server.SurveillanceCamera; using Content.Server.SurveillanceCamera;
@@ -23,7 +23,6 @@ using Robust.Shared.Timing;
using System.Linq; using System.Linq;
using Content.Shared.EntityEffects.Effects.Solution; using Content.Shared.EntityEffects.Effects.Solution;
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
using Content.Server.Body.Components; // Delta V
namespace Content.Server.Fluids.EntitySystems; namespace Content.Server.Fluids.EntitySystems;
@@ -268,8 +267,7 @@ public sealed class SmokeSystem : EntitySystem
if (!_solutionContainerSystem.ResolveSolution(entity, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution) || bloodSolution.AvailableVolume <= 0) if (!_solutionContainerSystem.ResolveSolution(entity, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution) || bloodSolution.AvailableVolume <= 0)
return; return;
var blockIngestion = _internals.AreInternalsWorking(entity) var blockIngestion = _internals.AreInternalsWorking(entity);
|| !HasComp<RespiratorComponent>(entity); // Starlight - Shadekin does not breathe and "AreInternalsWorking" does not check for that
var cloneSolution = solution.Clone(); var cloneSolution = solution.Clone();
var availableTransfer = FixedPoint2.Min(cloneSolution.Volume, component.TransferRate); var availableTransfer = FixedPoint2.Min(cloneSolution.Volume, component.TransferRate);
@@ -1,4 +1,3 @@
using Content.Server.RoundEnd; // DeltaV
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.GameTicking.Rules.Components; namespace Content.Server.GameTicking.Rules.Components;
@@ -44,16 +43,4 @@ public sealed partial class XenoborgsRuleComponent : Component
/// </summary> /// </summary>
[DataField] [DataField]
public bool XenoborgShuttleCalled = false; 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,20 +30,10 @@ public sealed partial class ZombieRuleComponent : Component
/// <summary> /// <summary>
/// DeltaV - The behavior of the round if all zombies are defeated. /// DeltaV - The behavior of the round if all zombies are defeated.
/// </summary> /// </summary>
[DataField]
public RoundEndBehavior ZombieRoundEndBehavior = RoundEndBehavior.BecomeSurvival; public RoundEndBehavior ZombieRoundEndBehavior = RoundEndBehavior.BecomeSurvival;
/// <summary> /// <summary>
/// DeltaV - The amount of time before the evac shuttle will arrive if the ZombieRoundEndBehavior is set to ShuttleCall. /// DeltaV - The amount of time before the evac shuttle will arrive if the ZombieRoundEndBehavior is set to ShuttleCall.
/// </summary> /// </summary>
[DataField]
public TimeSpan ZombieShuttleDelay = TimeSpan.FromMinutes(10); 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,17 +520,18 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
// BEGIN DeltaV - Detect Nukie Failure Better // 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 // 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>>(); var operatives = new List<Entity<NukeOperativeComponent, MobStateComponent, TransformComponent>>();
var operativesEnumerator = EntityQueryEnumerator<NukeOperativeComponent, MobStateComponent>(); var operativesEnumerator = EntityQueryEnumerator<NukeOperativeComponent, MobStateComponent, TransformComponent>();
while (operativesEnumerator.MoveNext(out var uid, out var nukeOp, out var mobState)) while (operativesEnumerator.MoveNext(out var uid, out var nukeOp, out var mobState, out var transform))
{ {
operatives.Add((uid, nukeOp, mobState)); operatives.Add((uid, nukeOp, mobState, transform));
} }
var operativesAlive = operatives var operativesAlive = operatives
.Any(op => op.Comp1.Running .Where(op => op.Comp3.MapID == shuttleMapId
&& op.Comp2.CurrentState == MobState.Alive || op.Comp3.MapID == targetStationMap)
&& TryComp<CuffableComponent>(op, out var cuffable) && cuffable.CuffedHandCount == 0); // in the case the crew keeps one alive .Any(op => op.Comp2.CurrentState == MobState.Alive && op.Comp1.Running &&
!TryComp<CuffableComponent>(op, out _)); // in the case the crew keeps one alive
// END DeltaV // END DeltaV
if (operativesAlive) if (operativesAlive)
@@ -42,14 +42,6 @@ public sealed class XenoborgsRuleSystem : GameRuleSystem<XenoborgsRuleComponent>
colorOverride: AnnouncmentColor); colorOverride: AnnouncmentColor);
ent.Comp.MothershipCoreDeathAnnouncmentSent = true; 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 // TODO: Refactor the end of round text
@@ -18,7 +18,6 @@ using Content.Shared.Zombies;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using System.Globalization; using System.Globalization;
using System.Linq; // DeltaV
namespace Content.Server.GameTicking.Rules; namespace Content.Server.GameTicking.Rules;
@@ -115,34 +114,27 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
/// </summary> /// </summary>
private void CheckRoundEnd(ZombieRuleComponent zombieRuleComponent) 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(); var healthy = GetHealthyHumans();
if (healthy.Count == 1) // Only one human left. spooky if (healthy.Count == 1) // Only one human left. spooky
_popup.PopupEntity(Loc.GetString("zombie-alone"), healthy[0], healthy[0]); _popup.PopupEntity(Loc.GetString("zombie-alone"), healthy[0], healthy[0]);
// BEGIN DeltaV - Change mode to survival if zombies die // BEGIN DeltaV - Change mode to survival if zombies die
if (zombieRuleComponent.IsRoundStartZombies) var infectedPercent = GetInfectedFraction(false);
if (Math.Round(infectedPercent, 0) == 0) // All zombies defeated
{ {
// Zombies have failed to launch and the mode has been switched to survival _roundEnd.DoRoundEndBehavior(zombieRuleComponent.ZombieRoundEndBehavior, zombieRuleComponent.ZombieShuttleDelay);
if (zombieRuleComponent.ZombieRoundEndBehavior == RoundEndBehavior.Nothing) zombieRuleComponent.ZombieRoundEndBehavior = RoundEndBehavior.Nothing; // stop this check in the future
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 // END DeltaV
if (GetInfectedFraction(false) > zombieRuleComponent.ZombieShuttleCallPercentage && !_roundEnd.IsRoundEndRequested()) if (infectedPercent > zombieRuleComponent.ZombieShuttleCallPercentage && !_roundEnd.IsRoundEndRequested()) // DeltaV - Move GetInfectedFraction to var
{ {
foreach (var station in _station.GetStations()) foreach (var station in _station.GetStations())
{ {
+1 -17
View File
@@ -29,7 +29,6 @@ using Content.Shared.Movement.Systems;
using Content.Shared.NameModifier.EntitySystems; using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Server.Preferences.Managers; // DeltaV using Content.Server.Preferences.Managers; // DeltaV
using Content.Shared._DV.Administration.EntitySystems; // DeltaV - Unorbitable
using Content.Shared.Storage.Components; using Content.Shared.Storage.Components;
using Content.Shared.Tag; using Content.Shared.Tag;
using Content.Shared.Warps; using Content.Shared.Warps;
@@ -42,7 +41,6 @@ using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Content.Server.Mobs; // Starlight
namespace Content.Server.Ghost namespace Content.Server.Ghost
{ {
@@ -76,7 +74,6 @@ namespace Content.Server.Ghost
[Dependency] private readonly GhostSpriteStateSystem _ghostState = default!; [Dependency] private readonly GhostSpriteStateSystem _ghostState = default!;
[Dependency] private readonly IServerPreferencesManager _preferencesManager = default!; // DeltaV [Dependency] private readonly IServerPreferencesManager _preferencesManager = default!; // DeltaV
[Dependency] private readonly IAdminManager _admin = default!; // DeltaV [Dependency] private readonly IAdminManager _admin = default!; // DeltaV
[Dependency] private readonly UnorbitableSystem _unorbitable = default!; // DeltaV - Unorbitable
private EntityQuery<GhostComponent> _ghostQuery; private EntityQuery<GhostComponent> _ghostQuery;
private EntityQuery<PhysicsComponent> _physicsQuery; private EntityQuery<PhysicsComponent> _physicsQuery;
@@ -343,11 +340,6 @@ namespace Content.Server.Ghost
return; return;
} }
// DeltaV - Unorbitable START
if (!_unorbitable.CanFollow(attached, realTarget))
return;
// DeltaV END
WarpTo(attached, realTarget); WarpTo(attached, realTarget);
} }
@@ -627,15 +619,7 @@ namespace Content.Server.Ghost
_damageable.GetTotalDamage((playerEntity.Value, damageable)); _damageable.GetTotalDamage((playerEntity.Value, damageable));
} }
// Starlight - Start DamageSpecifier damage = new(_prototypeManager.Index(AsphyxiationDamageType), dealtDamage);
//DamageSpecifier damage = new(_prototypeManager.Index(AsphyxiationDamageType), dealtDamage);
var damageType = _prototypeManager.Index(AsphyxiationDamageType);
if (TryComp<DeathgaspComponent>(playerEntity, out var deathgasp))
damageType = _prototypeManager.Index(deathgasp.DamageType);
DamageSpecifier damage = new(damageType, dealtDamage);
// Starlight - End
_damageable.ChangeDamage(playerEntity.Value, damage, true); _damageable.ChangeDamage(playerEntity.Value, damage, true);
} }
@@ -30,17 +30,6 @@ public sealed partial class EmergencyLightComponent : SharedEmergencyLightCompon
[DataField("chargingEfficiency")] [DataField("chargingEfficiency")]
public float ChargingEfficiency = 0.85f; 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() public Dictionary<EmergencyLightState, string> BatteryStateText = new()
{ {
{ EmergencyLightState.Full, "emergency-light-component-light-state-full" }, { EmergencyLightState.Full, "emergency-light-component-light-state-full" },
@@ -23,7 +23,6 @@ using Content.Server.Body.Systems;
// Begin DeltaV // Begin DeltaV
using Content.Server._DV.MedicalRecords; using Content.Server._DV.MedicalRecords;
using Content.Shared._DV.MedicalRecords; using Content.Shared._DV.MedicalRecords;
using Content.Shared.Chemistry.Components;
// End DeltaV // End DeltaV
namespace Content.Server.Medical; namespace Content.Server.Medical;
@@ -85,11 +84,11 @@ public sealed partial class HealthAnalyzerSystem : EntitySystem // DeltaV - Made
if (component.MaxScanRange != null && !_transformSystem.InRange(patientCoordinates, transform.Coordinates, component.MaxScanRange.Value)) if (component.MaxScanRange != null && !_transformSystem.InRange(patientCoordinates, transform.Coordinates, component.MaxScanRange.Value))
{ {
//Range too far, disable updates //Range too far, disable updates
PauseAnalyzingEntity((uid, component), patient); PauseAnalyzingEntity((uid, component), patient); // DeltaV - Analyzer Reactivation
continue; continue;
} }
component.IsAnalyzerActive = true; component.IsAnalyzerActive = true; // DeltaV - Analyzer Reactivation
UpdateScannedUser(uid, patient, true); UpdateScannedUser(uid, patient, true);
} }
} }
@@ -197,7 +196,7 @@ public sealed partial class HealthAnalyzerSystem : EntitySystem // DeltaV - Made
} }
/// <summary> /// <summary>
/// If the scanner is active, sends one last update and sets it to inactive. /// DeltaV - If the scanner is active, sends one last update and sets it to inactive.
/// </summary> /// </summary>
/// <param name="healthAnalyzer">The health analyzer that's receiving the updates</param> /// <param name="healthAnalyzer">The health analyzer that's receiving the updates</param>
/// <param name="target">The entity to analyze</param> /// <param name="target">The entity to analyze</param>
@@ -270,11 +269,9 @@ public sealed partial class HealthAnalyzerSystem : EntitySystem // DeltaV - Made
var bleeding = false; var bleeding = false;
var unrevivable = false; var unrevivable = false;
Solution? bloodSolution = null; // DeltaV - Health Analyzer Plus
if (TryComp<BloodstreamComponent>(entity, out var bloodstream) && if (TryComp<BloodstreamComponent>(entity, out var bloodstream) &&
_solutionContainerSystem.ResolveSolution(entity, bloodstream.BloodSolutionName, _solutionContainerSystem.ResolveSolution(entity, bloodstream.BloodSolutionName,
ref bloodstream.BloodSolution, out bloodSolution)) // DeltaV - Health Analyzer Plus ref bloodstream.BloodSolution, out var bloodSolution))
{ {
bloodAmount = _bloodstreamSystem.GetBloodLevel(entity); bloodAmount = _bloodstreamSystem.GetBloodLevel(entity);
bleeding = bloodstream.BleedAmount > 0; bleeding = bloodstream.BleedAmount > 0;
@@ -290,7 +287,6 @@ public sealed partial class HealthAnalyzerSystem : EntitySystem // DeltaV - Made
null, null,
bleeding, bleeding,
unrevivable, unrevivable,
bloodSolution, // DeltaV - Health Analyzer Plus
_medicalRecords.GetMedicalRecords(entity) // DeltaV - Medical Records _medicalRecords.GetMedicalRecords(entity) // DeltaV - Medical Records
); );
} }
+1 -9
View File
@@ -1,6 +1,4 @@
using Content.Shared.Chat.Prototypes; // Starlight using Content.Shared.Chat.Prototypes;
using Content.Shared.Damage.Prototypes; // Starlight
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Mobs; namespace Content.Server.Mobs;
@@ -23,10 +21,4 @@ public sealed partial class DeathgaspComponent : Component
/// </summary> /// </summary>
[DataField] [DataField]
public bool NeedsCritical = true; public bool NeedsCritical = true;
/// <summary>
/// Starlight - The damage that is taken when succumbing
/// </summary>
[DataField]
public ProtoId<DamageTypePrototype> DamageType = "Asphyxiation";
} }
@@ -55,13 +55,9 @@ public sealed class PseudoItemSystem : SharedPseudoItemSystem
protected override void OnGettingPickedUpAttempt(EntityUid uid, PseudoItemComponent component, GettingPickedUpAttemptEvent args) protected override void OnGettingPickedUpAttempt(EntityUid uid, PseudoItemComponent component, GettingPickedUpAttemptEvent args)
{ {
// Floof - changed this a bit to actually start a do-after
// Try to pick the entity up instead first // Try to pick the entity up instead first
if (args.User != args.Item if (args.User != args.Item && _carrying.TryCarry(args.User, uid))
&& TryComp<CarriableComponent>(uid, out var carriable)
&& _carrying.CanCarry(args.User, (uid, carriable)))
{ {
_carrying.StartCarryDoAfter(args.User, (uid, carriable));
args.Cancel(); args.Cancel();
return; return;
} }
@@ -209,24 +209,20 @@ namespace Content.Server.Psionics.Glimmer
private void OnDestroyed(EntityUid uid, SharedGlimmerReactiveComponent component, DestructionEventArgs args) private void OnDestroyed(EntityUid uid, SharedGlimmerReactiveComponent component, DestructionEventArgs args)
{ {
var proberCoords = Transform(uid).Coordinates; Spawn("MaterialBluespace1", Transform(uid).Coordinates);
var tier = _glimmerSystem.GetGlimmerTier(); var tier = _glimmerSystem.GetGlimmerTier();
if (tier < GlimmerTier.High) if (tier < GlimmerTier.High)
return; return;
var explosionMultiplier = 2; var totalIntensity = (float) (_glimmerSystem.Glimmer * 2);
if (_glimmerSystem.GetGlimmerTier() == GlimmerTier.Critical) // YOU DONE FUCKED UP var slope = (float) (11 - _glimmerSystem.Glimmer / 100);
explosionMultiplier = 3; var maxIntensity = 20;
var totalIntensity = (float)(_glimmerSystem.Glimmer * explosionMultiplier); var removed = (float) _glimmerSystem.Glimmer * _random.NextFloat(0.06f, 0.08f);
var slope = (float)(11 - _glimmerSystem.Glimmer / 100); _glimmerSystem.Glimmer -= (int) removed;
var maxIntensity = 75; // Same as syndicate bomb
var removed = _glimmerSystem.Glimmer * _random.NextFloat(0.06f, 0.08f);
_glimmerSystem.Glimmer -= (int)removed;
BeamRandomNearProber(uid, _glimmerSystem.Glimmer / 350, _glimmerSystem.Glimmer / 50); BeamRandomNearProber(uid, _glimmerSystem.Glimmer / 350, _glimmerSystem.Glimmer / 50);
_explosionSystem.QueueExplosion(uid, "Default", totalIntensity, slope, maxIntensity, addLog: true); _explosionSystem.QueueExplosion(uid, "Default", totalIntensity, slope, maxIntensity);
Spawn("MaterialBluespace1", proberCoords); // Congrats on your bluespace!
} }
private void OnUnanchorAttempt(EntityUid uid, SharedGlimmerReactiveComponent component, UnanchorAttemptEvent args) private void OnUnanchorAttempt(EntityUid uid, SharedGlimmerReactiveComponent component, UnanchorAttemptEvent args)
@@ -234,7 +230,7 @@ namespace Content.Server.Psionics.Glimmer
if (component.Locked) if (component.Locked)
{ {
_sharedAudioSystem.PlayPvs(component.ShockNoises, args.User); _sharedAudioSystem.PlayPvs(component.ShockNoises, args.User);
_electrocutionSystem.TryDoElectrocution(args.User, uid, _glimmerSystem.Glimmer / 200, TimeSpan.FromSeconds((float) _glimmerSystem.Glimmer / 100), false); _electrocutionSystem.TryDoElectrocution(args.User, null, _glimmerSystem.Glimmer / 200, TimeSpan.FromSeconds((float) _glimmerSystem.Glimmer / 100), false);
args.Cancel(); args.Cancel();
} }
} }
@@ -184,8 +184,7 @@ public sealed class OracleSystem : EntitySystem
while (i != 0) while (i != 0)
{ {
var entityToSpawn = _random.Next(0, 2) == 0 ? "MaterialBluespace1" : "CrystalNormality"; Spawn("MaterialBluespace1", Transform(user).Coordinates);
Spawn(entityToSpawn, Transform(user).Coordinates);
i--; i--;
} }
@@ -125,12 +125,12 @@ public sealed class ProjectileSystem : SharedProjectileSystem
RaiseLocalEvent(projectile, ref pierceEv); RaiseLocalEvent(projectile, ref pierceEv);
// If the object won't be destroyed, it "tanks" the penetration hit. // If the object won't be destroyed, it "tanks" the penetration hit.
if (damage.GetTotal() < damageRequired && !pierceEv.Pierced) // DeltaV - Addition of the NT-3 if (damage.GetTotal() < damageRequired)
{ {
return false; return false;
} }
if (!projectile.Comp.ProjectileSpent && !pierceEv.Pierced) // DeltaV - Addition of the NT-3 if (!projectile.Comp.ProjectileSpent)
{ {
projectile.Comp.PenetrationAmount += damageRequired; projectile.Comp.PenetrationAmount += damageRequired;
// The projectile has dealt enough damage to be spent. // The projectile has dealt enough damage to be spent.
@@ -139,7 +139,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem
return false; return false;
} }
if (projectile.Comp.ProjectileSpent) if (projectile.Comp.ProjectileSpent && pierceEv.Pierced) // DeltaV - Addition of the NT-3
{ {
return true; return true;
} }
@@ -22,7 +22,6 @@ using Content.Shared.StatusEffect;
using Content.Shared.Store.Components; using Content.Shared.Store.Components;
using Content.Shared.Stunnable; using Content.Shared.Stunnable;
using Content.Shared.Tag; using Content.Shared.Tag;
using Content.Shared.Weapons.Melee.Events; // DeltaV
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -65,7 +64,6 @@ public sealed partial class RevenantSystem : EntitySystem
SubscribeLocalEvent<RevenantComponent, StatusEffectAddedEvent>(OnStatusAdded); SubscribeLocalEvent<RevenantComponent, StatusEffectAddedEvent>(OnStatusAdded);
SubscribeLocalEvent<RevenantComponent, StatusEffectEndedEvent>(OnStatusEnded); SubscribeLocalEvent<RevenantComponent, StatusEffectEndedEvent>(OnStatusEnded);
SubscribeLocalEvent<RoundEndTextAppendEvent>(_ => MakeVisible(true)); SubscribeLocalEvent<RoundEndTextAppendEvent>(_ => MakeVisible(true));
SubscribeLocalEvent<RevenantComponent, MeleeAttackEvent>(OnMeleeAttack); // DeltaV
SubscribeLocalEvent<RevenantComponent, GetVisMaskEvent>(OnRevenantGetVis); SubscribeLocalEvent<RevenantComponent, GetVisMaskEvent>(OnRevenantGetVis);
@@ -133,14 +131,6 @@ public sealed partial class RevenantSystem : EntitySystem
ChangeEssenceAmount(uid, essenceDamage, component); 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) public bool ChangeEssenceAmount(EntityUid uid, FixedPoint2 amount, RevenantComponent? component = null, bool allowDeath = true, bool regenCap = false)
{ {
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
@@ -41,8 +41,7 @@ namespace Content.Server.StationEvents
if (TryComp<NextEventComponent>(uid, out var nextEventComponent) if (TryComp<NextEventComponent>(uid, out var nextEventComponent)
&& _event.TryGenerateRandomEvent(component.ScheduledGameRules, TimeSpan.FromSeconds(component.TimeUntilNextEvent)) is {} firstEvent) && _event.TryGenerateRandomEvent(component.ScheduledGameRules, TimeSpan.FromSeconds(component.TimeUntilNextEvent)) is {} firstEvent)
{ {
var nextEventTime = GameTicker.RoundDuration() + TimeSpan.FromSeconds(component.TimeUntilNextEvent); _chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", firstEvent), ("seconds", (int)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)); _next.UpdateNextEvent(nextEventComponent, firstEvent, GameTicker.RoundDuration() + TimeSpan.FromSeconds(component.TimeUntilNextEvent));
} }
// End DeltaV Additions // End DeltaV Additions
@@ -82,7 +81,7 @@ namespace Content.Server.StationEvents
if (_event.TryGenerateRandomEvent(eventScheduler.ScheduledGameRules, nextEventTime) is not {} generatedEvent) if (_event.TryGenerateRandomEvent(eventScheduler.ScheduledGameRules, nextEventTime) is not {} generatedEvent)
continue; continue;
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", generatedEvent), ("time", nextEventTime.ToString(@"hh\:mm\:ss")))); _chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", generatedEvent), ("seconds", (int)eventScheduler.TimeUntilNextEvent)));
// Cycle the stashed event with the new generated event and time. // Cycle the stashed event with the new generated event and time.
var storedEvent = _next.UpdateNextEvent(nextEventComponent, generatedEvent, nextEventTime); var storedEvent = _next.UpdateNextEvent(nextEventComponent, generatedEvent, nextEventTime);
if (string.IsNullOrEmpty(storedEvent)) //If there was no stored event don't try to run it. if (string.IsNullOrEmpty(storedEvent)) //If there was no stored event don't try to run it.
@@ -14,12 +14,9 @@ public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingS
[Dependency] private readonly IChatManager _chatManager = default!; // DeltaV [Dependency] private readonly IChatManager _chatManager = default!; // DeltaV
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EventManagerSystem _event = default!; [Dependency] private readonly EventManagerSystem _event = default!;
#if DEBUG // DeltaV - Only used if built by Debug
[Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly GameTicker _gameTicker = default!;
#endif
[Dependency] private readonly NextEventSystem _next = default!; // DeltaV [Dependency] private readonly NextEventSystem _next = default!; // DeltaV
[Dependency] private readonly IGameTiming _timing = default!; // DeltaV
/* DeltaV /* DeltaV
/// <summary> /// <summary>
/// Returns the ChaosModifier which increases as round time increases to a point. /// Returns the ChaosModifier which increases as round time increases to a point.
@@ -53,8 +50,7 @@ public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingS
if (TryComp<NextEventComponent>(uid, out var nextEventComponent) if (TryComp<NextEventComponent>(uid, out var nextEventComponent)
&& _event.TryGenerateRandomEvent(component.ScheduledGameRules, TimeSpan.FromSeconds(component.TimeUntilNextEvent)) is {} firstEvent) && _event.TryGenerateRandomEvent(component.ScheduledGameRules, TimeSpan.FromSeconds(component.TimeUntilNextEvent)) is {} firstEvent)
{ {
var nextEventTime = GameTicker.RoundDuration() + TimeSpan.FromSeconds(component.TimeUntilNextEvent); _chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", firstEvent), ("seconds", (int)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)); _next.UpdateNextEvent(nextEventComponent, firstEvent, GameTicker.RoundDuration() + TimeSpan.FromSeconds(component.TimeUntilNextEvent));
} }
// End DeltaV Additions: init NextEventComp // End DeltaV Additions: init NextEventComp
@@ -87,7 +83,7 @@ public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingS
if (_event.TryGenerateRandomEvent(scheduler.ScheduledGameRules, nextEventTime) is not {} generatedEvent) if (_event.TryGenerateRandomEvent(scheduler.ScheduledGameRules, nextEventTime) is not {} generatedEvent)
continue; continue;
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", generatedEvent), ("time", nextEventTime.ToString(@"hh\:mm\:ss")))); _chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", generatedEvent), ("seconds", (int)scheduler.TimeUntilNextEvent)));
// Cycle the stashed event with the new generated event and time. // Cycle the stashed event with the new generated event and time.
string? storedEvent = _next.UpdateNextEvent(nextEventComponent, generatedEvent, nextEventTime); string? storedEvent = _next.UpdateNextEvent(nextEventComponent, generatedEvent, nextEventTime);
if (string.IsNullOrEmpty(storedEvent)) //If there was no stored event don't try to run it. if (string.IsNullOrEmpty(storedEvent)) //If there was no stored event don't try to run it.
@@ -118,28 +114,13 @@ public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingS
// Begin DeltaV Additions // Begin DeltaV Additions
var averageTimeUntilNextEvent = component.TimeKeyPoints[0].Y; var averageTimeUntilNextEvent = component.TimeKeyPoints[0].Y;
var timeUntilNextEventDeviation = _random.NextFloat(-1f, 1f) * component.TimeDeviation; var timeUntilNextEventDeviation = _random.NextFloat(-1f, 1f) * component.TimeDeviation;
var roundTime = (float)_gameTicker.RoundDuration().TotalMinutes;
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; var absoluteTimePoint = 0f;
foreach (var point in component.TimeKeyPoints) foreach (var point in component.TimeKeyPoints)
{ {
absoluteTimePoint += point.X; absoluteTimePoint += point.X;
if (ruleTime.TotalMinutes >= absoluteTimePoint) if (roundTime >= absoluteTimePoint)
averageTimeUntilNextEvent = point.Y; averageTimeUntilNextEvent = point.Y;
} }
@@ -0,0 +1,43 @@
using Content.Shared._CD.Silicons.Borgs;
using Content.Shared.Inventory;
namespace Content.Server._CD.Silicons.Borgs;
/// <summary>
/// Server-side logic that shouldn't be exposed to the client.
/// </summary>
public sealed class BorgSwitchableSubstypeSystem : SharedBorgSwitchableSubtypeSystem
{
[Dependency] private readonly InventorySystem _inventorySystem = default!;
protected override void SelectBorgSubtype(Entity<BorgSwitchableSubtypeComponent> ent)
{
if (ent.Comp.BorgSubtype == null)
return;
if (!Prototypes.Index(ent.Comp.BorgSubtype.Value)
.TryGetComponent<BorgSubtypeDefinitionComponent>(out var borgSubtype, ComponentFactory))
return;
// Configure special components
if (Prototypes.TryIndex(ent.Comp.BorgSubtype, out var previousPrototype) &&
previousPrototype.TryGetComponent<BorgSubtypeDefinitionComponent>(out var previousSubtype, ComponentFactory))
{
if (previousSubtype.AddComponents is { } removeComponents)
EntityManager.RemoveComponents(ent, removeComponents);
}
if (borgSubtype.AddComponents is { } addComponents)
{
EntityManager.AddComponents(ent, addComponents);
}
// inventory template configuration (hats spacing)
if (TryComp(ent, out InventoryComponent? inventory))
{
_inventorySystem.SetTemplateId((ent.Owner, inventory), borgSubtype.InventoryTemplateId);
}
base.SelectBorgSubtype(ent);
}
}
@@ -0,0 +1,32 @@
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);
}
}
@@ -1,184 +0,0 @@
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}");
}
}
}
@@ -75,7 +75,8 @@ public sealed partial class CosmicFinaleComponent : Component
{ "Blunt", 2.25}, { "Blunt", 2.25},
{ "Cold", 2.25}, { "Cold", 2.25},
{ "Radiation", 2.25}, { "Radiation", 2.25},
{ "Asphyxiation", 2.25} { "Asphyxiation", 2.25},
{ "Ion", 2.25}
} }
}; };
} }
@@ -174,7 +174,7 @@ public sealed partial class CosmicCultSystem : SharedCosmicCultSystem
if (!EntityIsCultist(args.Equipee)) if (!EntityIsCultist(args.Equipee))
{ {
_statusEffects.TryAddStatusEffect<CosmicEntropyDebuffComponent>(args.Equipee, EntropicDegen, TimeSpan.FromDays(1), true); // TimeSpan.MaxValue causes a crash here, so we use FromDays(1) instead. _statusEffects.TryAddStatusEffect<CosmicEntropyDebuffComponent>(args.Equipee, EntropicDegen, TimeSpan.FromDays(1), true); // TimeSpan.MaxValue causes a crash here, so we use FromDays(1) instead.
if (TryComp<CosmicEntropyDebuffComponent>(args.Equipee, out var comp)) comp.Degen = new(){DamageDict = new(){{"Cold", 0.5}, {"Asphyxiation", 1.5}}}; if (TryComp<CosmicEntropyDebuffComponent>(args.Equipee, out var comp)) comp.Degen = new(){DamageDict = new(){{"Cold", 0.5}, {"Asphyxiation", 1.5}, {"Ion", 1.5}}};
} }
} }
@@ -188,7 +188,7 @@ public sealed partial class CosmicCultSystem : SharedCosmicCultSystem
if (!EntityIsCultist(args.User)) if (!EntityIsCultist(args.User))
{ {
_statusEffects.TryAddStatusEffect<CosmicEntropyDebuffComponent>(args.User, EntropicDegen, TimeSpan.FromDays(1), true); _statusEffects.TryAddStatusEffect<CosmicEntropyDebuffComponent>(args.User, EntropicDegen, TimeSpan.FromDays(1), true);
if (TryComp<CosmicEntropyDebuffComponent>(args.User, out var comp)) comp.Degen = new(){DamageDict = new(){{"Cold", 0.5}, {"Asphyxiation", 1.5}}}; if (TryComp<CosmicEntropyDebuffComponent>(args.User, out var comp)) comp.Degen = new(){DamageDict = new(){{"Cold", 0.5}, {"Asphyxiation", 1.5}, {"Ion", 1.5}}};
_popup.PopupEntity(Loc.GetString("cosmiccult-gear-pickup", ("ITEM", args.Equipped)), args.User, args.User, PopupType.MediumCaution); _popup.PopupEntity(Loc.GetString("cosmiccult-gear-pickup", ("ITEM", args.Equipped)), args.User, args.User, PopupType.MediumCaution);
} }
} }
@@ -75,7 +75,8 @@ public sealed class CosmicRiftSystem : EntitySystem
if (TryComp<CosmicEntropyDebuffComponent>(mob, out var debuff)) debuff.Degen = if (TryComp<CosmicEntropyDebuffComponent>(mob, out var debuff)) debuff.Degen =
new(){DamageDict = new(){ new(){DamageDict = new(){
{"Cold", 0.05 * damageMultiplier}, {"Cold", 0.05 * damageMultiplier},
{"Asphyxiation", 0.15 * damageMultiplier} {"Asphyxiation", 0.15 * damageMultiplier},
{"Ion", 0.15 * damageMultiplier}
}}; }};
} }
} }
@@ -33,7 +33,7 @@ public sealed partial class GameTicker
public bool ConvertRoundToSurvival() public bool ConvertRoundToSurvival()
{ {
// Ramping scheduler is already added. Do nothing. // Ramping scheduler is already added. Do nothing.
if (IsGameRuleAdded(RampingSchedulerProto)) if (IsGameRuleActive(RampingSchedulerProto))
{ {
_chatManager.SendAdminAlert("RampingStationEventScheduler detected. No rules added."); _chatManager.SendAdminAlert("RampingStationEventScheduler detected. No rules added.");
return true; return true;
@@ -44,7 +44,7 @@ public sealed partial class GameTicker
var rampingSchedulerStart = _gameTiming.CurTime.Add(GracePeriod); var rampingSchedulerStart = _gameTiming.CurTime.Add(GracePeriod);
EnsureComp<DelayedStartRuleComponent>(rampingScheduler).RuleStartTime = rampingSchedulerStart; EnsureComp<DelayedStartRuleComponent>(rampingScheduler).RuleStartTime = rampingSchedulerStart;
_chatManager.SendAdminAlert($"Major antag defeated. Converting to survival at {(RoundDuration() + GracePeriod).ToString(@"hh\:mm\:ss")}."); _chatManager.SendAdminAlert($"Major antag defeated. Converting to survival at {rampingSchedulerStart}.");
// End Basic Rules // End Basic Rules
var basicRules = EntityQueryEnumerator<BasicStationEventSchedulerComponent>(); var basicRules = EntityQueryEnumerator<BasicStationEventSchedulerComponent>();
@@ -1,88 +0,0 @@
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,7 +8,6 @@ using Content.Server.NodeContainer.Nodes;
using Content.Shared._DV.NodeCrawl; using Content.Shared._DV.NodeCrawl;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.NodeContainer; using Content.Shared.NodeContainer;
using Content.Shared.Polymorph;
using Content.Shared.Zombies; using Content.Shared.Zombies;
using Robust.Shared.Reflection; using Robust.Shared.Reflection;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -31,7 +30,6 @@ public sealed class NodeCrawlSystem : SharedNodeCrawlSystem
SubscribeLocalEvent<NodeCrawlerComponent, InhaleLocationEvent>(OnInhaleLocation); SubscribeLocalEvent<NodeCrawlerComponent, InhaleLocationEvent>(OnInhaleLocation);
SubscribeLocalEvent<NodeCrawlerComponent, ExhaleLocationEvent>(OnExhaleLocation); SubscribeLocalEvent<NodeCrawlerComponent, ExhaleLocationEvent>(OnExhaleLocation);
SubscribeLocalEvent<NodeCrawlerComponent, AtmosExposedGetAirEvent>(OnGetAir); SubscribeLocalEvent<NodeCrawlerComponent, AtmosExposedGetAirEvent>(OnGetAir);
SubscribeLocalEvent<NodeCrawlerComponent, PolymorphActionEvent>(OnPolymorph);
SubscribeLocalEvent<NodeCrawlerComponent, EntityZombifiedEvent>(OnZombify); SubscribeLocalEvent<NodeCrawlerComponent, EntityZombifiedEvent>(OnZombify);
} }
@@ -224,15 +222,4 @@ public sealed class NodeCrawlSystem : SharedNodeCrawlSystem
ent.Comp.EnterDelay = ent.Comp.ZombieEnterDelay; ent.Comp.EnterDelay = ent.Comp.ZombieEnterDelay;
Dirty(ent); 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);
}
} }
@@ -1,5 +1,4 @@
using Content.Shared.Tag; using Content.Shared.Tag;
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Server._DV.Projectiles.Components; namespace Content.Server._DV.Projectiles.Components;
@@ -22,14 +21,10 @@ public sealed partial class PiercingProjectileComponent : Component
public float PierceCounter; public float PierceCounter;
/// <summary> /// <summary>
/// The whitelist for checking what increments the <see cref="PierceCounter"/>. /// The tag that will cause the piercing bullet to increment it's <see cref="PierceCounter"/>.
/// </summary> /// </summary>
/// <example>
/// If this has the tag "Wall" in it, any entity with the tag "Wall" will increment <see cref="PierceCounter"/>
/// upon being hit.
/// </example>
[DataField] [DataField]
public EntityWhitelist PierceCounterWhitelist; public List<ProtoId<TagPrototype>> PierceBlockTag = ["Wall", "Window"];
/// <summary> /// <summary>
/// The number of entities it is allowed to pierce before being deleted. /// The number of entities it is allowed to pierce before being deleted.
@@ -3,7 +3,7 @@ using Content.Shared.FixedPoint;
namespace Content.Server._DV.Projectiles.Events; namespace Content.Server._DV.Projectiles.Events;
/// <summary> /// <summary>
/// Raised when a piercing projectile that doesn't follow upstream piercing rules hits an entity. /// Raised when a piercing projectile hits an entity that doesn't follow upstream piercing rules.
/// </summary> /// </summary>
[ByRefEvent] [ByRefEvent]
public record struct ProjectilePierceEvent(EntityUid Target, FixedPoint2 RequiredDamage, bool Pierced = false); public record struct ProjectilePierceEvent(EntityUid Target, FixedPoint2 RequiredDamage, bool Pierced = false);
@@ -1,33 +1,29 @@
using Content.Server._DV.Projectiles.Components; using Content.Server._DV.Projectiles.Components;
using Content.Server._DV.Projectiles.Events; using Content.Server._DV.Projectiles.Events;
using Content.Shared.Whitelist; using Content.Shared.Tag;
namespace Content.Server._DV.Projectiles.Systems; namespace Content.Server._DV.Projectiles.Systems;
public sealed class PiercingProjectileSystem : EntitySystem public sealed class PiercingProjectileSystem : EntitySystem
{ {
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly TagSystem _tagSystem = default!;
// Mobs return a required Damage amount of Float.MaxValue. Therefore, we need to check for absurdly high values.
private readonly int _indestructibleNumber = 20000000;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<PiercingProjectileComponent, ProjectilePierceEvent>(OnPierce); SubscribeLocalEvent<PiercingProjectileComponent, ProjectilePierceEvent>(OnPierce);
} }
private void OnPierce(Entity<PiercingProjectileComponent> bullet, ref ProjectilePierceEvent args) private void OnPierce(Entity<PiercingProjectileComponent> bullet, ref ProjectilePierceEvent args)
{ {
// If the target doesn't have any tags to stop the bullet from piercing, it's automatically true. // If the target doesn't have any tags to stop the bullet from piercing, it's automatically true.
if (_whitelist.IsWhitelistFail(bullet.Comp.PierceCounterWhitelist, args.Target)) if (!_tagSystem.HasAnyTag(args.Target, bullet.Comp.PierceBlockTag))
{ {
args.Pierced = true; args.Pierced = true;
return; return;
} }
// If it does have the tag to stop it and enough health to count as "strongly armored", it'll block the bullet. // If it does have the tag to stop it and enough health to count as "strongly armored", it'll block the bullet.
if (bullet.Comp.HealthThreshold < args.RequiredDamage && args.RequiredDamage < _indestructibleNumber) if (bullet.Comp.HealthThreshold < args.RequiredDamage)
return; return;
if (bullet.Comp.Direction == null) // Get the direction of the bullet to determine which walls count. if (bullet.Comp.Direction == null) // Get the direction of the bullet to determine which walls count.
@@ -1,111 +0,0 @@
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));
}
}
}
@@ -0,0 +1,5 @@
using Content.Shared._DV.Silicons;
namespace Content.Server._DV.Silicons;
public sealed class SiliconEmpSystem : SharedSiliconEmpSystem;
@@ -32,6 +32,7 @@ public sealed class StationExfiltrationSystem : EntitySystem
[Dependency] private readonly ShuttleSystem _shuttle = default!; [Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly NavMapSystem _navMap = default!; [Dependency] private readonly NavMapSystem _navMap = default!;
[Dependency] private readonly CommunicationsConsoleSystem _communicationsConsole = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -168,8 +169,7 @@ 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); _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);
} }
var evt = new StationExfiltrationChangedEvent((ent, ent.Comp), true); _communicationsConsole.UpdateCommsConsoleInterface();
RaiseLocalEvent(ref evt);
} }
public void Recall(Entity<StationExfiltrationComponent?> ent) public void Recall(Entity<StationExfiltrationComponent?> ent)
@@ -180,10 +180,6 @@ public sealed class StationExfiltrationSystem : EntitySystem
ent.Comp.ArrivalTime = null; ent.Comp.ArrivalTime = null;
_chat.DispatchStationAnnouncement(ent, Loc.GetString(ent.Comp.RecalledAnnouncement, ("station", Name(ent))), sender: Loc.GetString(ent.Comp.Sender), colorOverride: Color.Gold); _chat.DispatchStationAnnouncement(ent, Loc.GetString(ent.Comp.RecalledAnnouncement, ("station", Name(ent))), sender: Loc.GetString(ent.Comp.Sender), colorOverride: Color.Gold);
var evt = new StationExfiltrationChangedEvent((ent, ent.Comp), false); _communicationsConsole.UpdateCommsConsoleInterface();
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._DV.StationEvents.Components;
using Content.Server.Antag; using Content.Server.Antag;
using Content.Server.Communications;
using Content.Server.StationEvents.Events; using Content.Server.StationEvents.Events;
using Content.Shared._DV.Communications;
using Content.Shared.Forensics.Components; using Content.Shared.Forensics.Components;
using Content.Shared.GameTicking.Components; using Content.Shared.GameTicking.Components;
using Content.Shared.Ghost; using Content.Shared.Ghost;
@@ -43,7 +43,7 @@ public sealed class FugitiveRule : StationEventSystem<FugitiveRuleComponent>
ChatSystem.DispatchGlobalAnnouncement(announcement, sender: sender, colorOverride: comp.Color); ChatSystem.DispatchGlobalAnnouncement(announcement, sender: sender, colorOverride: comp.Color);
// send the report to every comms console on the station // send the report to every comms console on the station
var query = EntityQueryEnumerator<TransformComponent, DVCommunicationsConsoleComponent>(); var query = EntityQueryEnumerator<TransformComponent, CommunicationsConsoleComponent>();
var consoles = new List<TransformComponent>(); var consoles = new List<TransformComponent>();
while (query.MoveNext(out var console, out var xform, out _)) while (query.MoveNext(out var console, out var xform, out _))
{ {
@@ -109,10 +109,8 @@ public sealed class FugitiveRule : StationEventSystem<FugitiveRuleComponent>
var report = new FormattedMessage(); var report = new FormattedMessage();
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-title")); report.AddMarkupOrThrow(Loc.GetString("fugitive-report-title"));
report.PushNewline(); report.PushNewline();
report.PushNewline();
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-first-line")); report.AddMarkupOrThrow(Loc.GetString("fugitive-report-first-line"));
report.PushNewline(); report.PushNewline();
report.PushNewline();
if (!TryComp<HumanoidProfileComponent>(uid, out var humanoid)) if (!TryComp<HumanoidProfileComponent>(uid, out var humanoid))
{ {
@@ -123,28 +121,22 @@ public sealed class FugitiveRule : StationEventSystem<FugitiveRuleComponent>
var species = PrototypeManager.Index(humanoid.Species); var species = PrototypeManager.Index(humanoid.Species);
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-morphotype", ("species", Loc.GetString(species.Name)))); report.AddMarkupOrThrow(Loc.GetString("fugitive-report-morphotype", ("species", Loc.GetString(species.Name))));
report.PushNewline();
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-age", ("age", humanoid.Age))); report.AddMarkupOrThrow(Loc.GetString("fugitive-report-age", ("age", humanoid.Age)));
report.PushNewline();
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-sex", ("sex", humanoid.Sex))); report.AddMarkupOrThrow(Loc.GetString("fugitive-report-sex", ("sex", humanoid.Sex)));
report.PushNewline();
if (TryComp<PhysicsComponent>(uid, out var physics)) if (TryComp<PhysicsComponent>(uid, out var physics))
{
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-weight", ("weight", Math.Round(physics.FixturesMass)))); report.AddMarkupOrThrow(Loc.GetString("fugitive-report-weight", ("weight", Math.Round(physics.FixturesMass))));
report.PushNewline();
}
// add a random identifying quality that officers can use to track them down // add a random identifying quality that officers can use to track them down
report.AddMarkupOrThrow(RobustRandom.Next(0, 2) switch report.AddMarkupOrThrow(RobustRandom.Next(0, 2) switch
{ {
0 => Loc.GetString("fugitive-report-detail-dna", ("dna", GetDNA(uid))), 0 => Loc.GetString("fugitive-report-detail-dna", ("dna", GetDNA(uid))),
_ => Loc.GetString("fugitive-report-detail-prints", ("prints", GetPrints(uid))) _ => Loc.GetString("fugitive-report-detail-prints", ("prints", GetPrints(uid)))
}); });
report.PushNewline();
report.PushNewline(); report.PushNewline();
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-crimes-header")); report.AddMarkupOrThrow(Loc.GetString("fugitive-report-crimes-header"));
report.PushNewline();
// generate some random crimes to avoid this situation // generate some random crimes to avoid this situation
// "officer what are my charges?" // "officer what are my charges?"
// "uh i dunno a piece of paper said to arrest you thats it" // "uh i dunno a piece of paper said to arrest you thats it"
@@ -180,7 +172,6 @@ public sealed class FugitiveRule : StationEventSystem<FugitiveRuleComponent>
{ {
var count = RobustRandom.Next(rule.MinCounts, rule.MaxCounts + 1); var count = RobustRandom.Next(rule.MinCounts, rule.MaxCounts + 1);
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-crime", ("crime", Loc.GetString(crime)), ("count", count))); report.AddMarkupOrThrow(Loc.GetString("fugitive-report-crime", ("crime", Loc.GetString(crime)), ("count", count)));
report.PushNewline();
} }
} }
} }
@@ -1,44 +0,0 @@
using Content.Shared.Store;
using Content.Shared.Store.Components;
using Robust.Shared.Prototypes;
namespace Content.Server._DV.Store.Conditions;
/// <summary>
/// XOR (eXclusive-OR) Buy Condition
/// A condition where that blocks buying a listing if another listing
/// specified is bought before it. Used to enforce that a traitor
/// can only buy 1 of X options.
///
/// Primarily used for choose-your-own-adventure DAGD Implants.
/// </summary>
public sealed partial class XORBuyCondition : ListingCondition
{
/// <summary>
/// Listing(s) that if bought, block this purchase, if any.
/// </summary>
[DataField(required: true)]
public HashSet<ProtoId<ListingPrototype>>? Listings;
public override bool Condition(ListingConditionArgs args)
{
if (!args.EntityManager.TryGetComponent<StoreComponent>(args.StoreEntity, out var storeComp))
return false;
var allListings = storeComp.FullListingsCatalog;
if (Listings != null)
{
foreach (var blacklistListing in Listings)
{
foreach (var listing in allListings)
{
if (listing.ID == blacklistListing.Id && listing.PurchaseAmount > 0)
return false; // it was purchased, so return false to fail the condition
}
}
}
return true; // If no purchases were found, return true
}
}
-163
View File
@@ -1,163 +0,0 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
using System.Text.RegularExpressions;
using Content.Server.Administration;
using Content.Shared.Administration;
using Robust.Shared.Console;
using Robust.Shared.Maths;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Errors;
using Robust.Shared.Utility;
namespace Content.Server._DV.Toolshed;
/// <summary>
/// An extended version of the engine's <c>do</c> command. Runs a console command once per piped value,
/// substituting <c>$NAME</c> tokens in the command string first.
/// </summary>
/// <remarks>
/// On top of the built-in tokens that <c>do</c> supports (<c>$ID</c>, <c>$PID</c>, <c>$WX</c>, <c>$WY</c>,
/// <c>$LX</c>, <c>$LY</c>, <c>$SELF</c>), any other <c>$name</c> is resolved as a toolshed variable and
/// converted to a string. Tokens that don't resolve to anything are left untouched.
///
/// Where the command actually runs is explicit:
/// <list type="bullet">
/// <item><c>doext:client</c> sends it back down to the calling user's own client to run locally. This is what
/// you want for client-side commands like <c>exec</c>, which resolve paths against the client's user data.</item>
/// <item><c>doext:server</c> runs it server-side as the calling session, like <c>do</c> does.</item>
/// </list>
/// </remarks>
/// <example><code>
/// self doext:client "exec /Script_$var.txt"
/// i 5 => $count; self doext:server "somecommand $count"
/// </code></example>
[ToolshedCommand(Name = "doext"), AdminCommand(AdminFlags.Debug)]
public sealed class DoExtCommand : ToolshedCommand
{
/// <summary>
/// Matches a <c>$name</c> token. Names use the same character set as toolshed variable names, so this
/// matches whole identifiers - <c>$IDLE</c> resolves as "IDLE" rather than being mangled into <c>$ID</c>.
/// </summary>
private static readonly Regex TokenRegex = new(@"\$(\w+)", RegexOptions.Compiled);
[Dependency] private readonly IConsoleHost _console = default!;
private SharedTransformSystem? _xform;
/// <summary>
/// Sends the command back down to the calling user's client, which runs it locally.
/// </summary>
[CommandImplementation("client"), TakesPipedTypeAsGeneric]
public IEnumerable<T> Client<T>(IInvocationContext ctx, [PipedArgument] IEnumerable<T> input, string command)
{
// No session means there's no client to hand this back to - e.g. the server console.
if (ctx.Session is not { } session)
{
ctx.ReportError(new NoClientSessionError());
yield break;
}
foreach (var value in input)
{
_console.RemoteExecuteCommand(session, Substitute(command, value, ctx));
yield return value;
}
}
/// <summary>
/// Runs the command on the server as the calling session.
/// </summary>
[CommandImplementation("server"), TakesPipedTypeAsGeneric]
public IEnumerable<T> Server<T>(IInvocationContext ctx, [PipedArgument] IEnumerable<T> input, string command)
{
foreach (var value in input)
{
_console.ExecuteCommand(ctx.Session, Substitute(command, value, ctx));
yield return value;
}
}
private string Substitute<T>(string command, T value, IInvocationContext ctx)
{
return TokenRegex.Replace(command, match =>
{
var name = match.Groups[1].Value;
if (TryGetBuiltin(name, value, ctx, out var builtin))
return builtin;
// Anything else is looked up as a toolshed variable and implicitly stringified.
if (ctx.ReadVar(name) is { } variable)
return Stringify(variable);
// Unresolved, so leave the token alone rather than silently blanking it.
return match.Value;
});
}
private bool TryGetBuiltin<T>(string name, T value, IInvocationContext ctx, [NotNullWhen(true)] out string? result)
{
switch (name)
{
case "SELF":
result = Stringify(value);
return true;
case "PID":
result = (ctx.Session?.AttachedEntity ?? EntityUid.Invalid).ToString();
return true;
}
// The remaining tokens are all positional, so they only apply when piping entities.
if (value is not EntityUid uid)
{
result = null;
return false;
}
switch (name)
{
case "ID":
result = uid.ToString();
return true;
case "WX":
result = Number(WorldPosition(uid).X);
return true;
case "WY":
result = Number(WorldPosition(uid).Y);
return true;
case "LX":
result = Number(Transform(uid).Coordinates.X);
return true;
case "LY":
result = Number(Transform(uid).Coordinates.Y);
return true;
}
result = null;
return false;
}
private Vector2 WorldPosition(EntityUid uid)
{
_xform ??= GetSys<SharedTransformSystem>();
return _xform.GetWorldPosition(uid);
}
private static string Number(float value) => value.ToString(CultureInfo.InvariantCulture);
private static string Stringify(object? value) => value?.ToString() ?? string.Empty;
}
public sealed class NoClientSessionError : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromUnformatted("There is no client to run this on. doext:client must be run by a player, not the server console.");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}
-136
View File
@@ -1,136 +0,0 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Content.Server.Administration;
using Content.Shared.Administration;
using Robust.Shared.Console;
using Robust.Shared.Maths;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Errors;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Server._DV.Toolshed;
/// <summary>
/// Reads the value of a single field or property off of the piped input, by name.
/// Member names are resolved the same way <c>vvread</c> does: the member must be exposed
/// via <c>[ViewVariables]</c>, <c>[DataField]</c>, or <c>[IncludeDataField]</c>.
/// </summary>
/// <example><code>ent 12345 . Name</code></example>
[ToolshedCommand(Name = "."), AdminCommand(AdminFlags.Debug)]
public sealed class FieldCommand : ToolshedCommand
{
[CommandImplementation]
public object? Field([PipedArgument] object? value, [CommandArgument(typeof(FieldNameParser))] string field, IInvocationContext ctx)
{
if (value == null)
{
ctx.ReportError(new NullInputFieldError());
return null;
}
var type = value.GetType();
var member = GetSingleMember(type, field);
// Restrict to members that vvread would let you read, so this can't be used to peek at arbitrary internals.
if (member == null || !ViewVariablesUtility.TryGetViewVariablesAccess(member, out _))
{
ctx.ReportError(new NoSuchFieldError(type, field));
return null;
}
return member switch
{
FieldInfo f => f.GetValue(value),
PropertyInfo p => p.GetValue(value),
_ => null,
};
}
/// <summary>
/// Finds the field or property with the given name, mirroring the resolution used by <c>vvread</c>.
/// </summary>
private static MemberInfo? GetSingleMember(Type type, string member)
{
var members = type
.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(m => m.Name == member && m is FieldInfo or PropertyInfo)
.ToArray();
if (members.Length == 0)
return null;
// In case there's member hiding going on, grab the one declared by the type of the object by default.
return members.Length > 1
? members.FirstOrDefault(m => m.DeclaringType == type) ?? members[0]
: members[0];
}
}
/// <summary>
/// Parses a field name as a bare identifier (letters, digits, underscore) so that the <c>.</c> command doesn't
/// require the name to be wrapped in quotes like the default <see cref="string"/> parser does.
/// </summary>
public sealed class FieldNameParser : CustomTypeParser<string>
{
public override bool TryParse(ParserContext ctx, [NotNullWhen(true)] out string? result)
{
ctx.ConsumeWhitespace();
result = ctx.GetWord(ParserContext.IsToken);
if (result != null)
return true;
if (ctx.PeekRune() is null)
ctx.Error = new OutOfInputError();
else
ctx.Error = new InvalidFieldNameError();
ctx.Error.Contextualize(ctx.Input, (ctx.Index, ctx.Index + 1));
return false;
}
public override CompletionResult? TryAutocomplete(ParserContext ctx, CommandArgument? arg)
{
return CompletionResult.FromHint(GetArgHint(arg));
}
}
public sealed class InvalidFieldNameError : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromUnformatted("Expected a field name (letters, digits, or underscores).");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}
public sealed class NullInputFieldError : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromUnformatted("Cannot read a field off of a null input.");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}
public sealed class NoSuchFieldError(Type type, string field) : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromUnformatted($"Type {type.Name} has no readable field or property named '{field}'. It must be exposed via [ViewVariables], [DataField], or [IncludeDataField].");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}
@@ -1,5 +0,0 @@
using Content.Shared._Floof.OfferItem;
namespace Content.Server._Floof.OfferItem;
public sealed partial class OfferItemSystem : SharedOfferItemSystem;
@@ -13,7 +13,7 @@ public sealed partial class RevenantStasisComponent : Component
public Entity<RevenantComponent> Revenant; public Entity<RevenantComponent> Revenant;
[ViewVariables(VVAccess.ReadOnly)] [ViewVariables(VVAccess.ReadOnly)]
public TimeSpan StasisDuration = TimeSpan.FromSeconds(100); // DeltaV - change from 60 to 100s public TimeSpan StasisDuration = TimeSpan.FromSeconds(60);
public RevenantStasisComponent(TimeSpan stasisDuration, Entity<RevenantComponent> revenant) public RevenantStasisComponent(TimeSpan stasisDuration, Entity<RevenantComponent> revenant)
{ {

Some files were not shown because too many files have changed in this diff Show More