mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
Merge remote-tracking branch 'refs/remotes/upstream/master' into upstream-sync
# Conflicts: # .github/CODEOWNERS # Content.Client/Administration/UI/Tabs/PanicBunkerTab/PanicBunkerTab.xaml.cs # Content.Client/Lobby/LobbyUIController.cs # Content.Server/Administration/Systems/AdminSystem.cs # Content.Server/Connection/ConnectionManager.cs # Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs # Content.Shared/Administration/Events/PanicBunkerChangedEvent.cs # Content.Shared/Preferences/HumanoidCharacterProfile.cs # Resources/Prototypes/Accents/word_replacements.yml # Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml # Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/duffelbag.yml # Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/satchel.yml # Resources/Prototypes/Entities/Mobs/base.yml # Resources/Prototypes/Roles/Antags/nukeops.yml # Resources/Prototypes/Roles/Antags/revolutionary.yml # Resources/Prototypes/Roles/Antags/traitor.yml # Resources/Prototypes/Roles/Antags/zombie.yml # Resources/Prototypes/Roles/Jobs/Security/security_cadet.yml # Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml # Resources/ServerInfo/Guidebook/Cargo/Cargo.xml # Resources/ServerInfo/Guidebook/Cargo/CargoBounties.xml # Resources/ServerInfo/Guidebook/Cargo/Salvage.xml # Resources/ServerInfo/Guidebook/Controls/Controls.xml # Resources/ServerInfo/Guidebook/Controls/Radio.xml # Resources/ServerInfo/Guidebook/Engineering/Engineering.xml # Resources/ServerInfo/Guidebook/Engineering/Fires.xml # Resources/ServerInfo/Guidebook/Engineering/Singularity.xml # Resources/ServerInfo/Guidebook/Jobs.xml # Resources/ServerInfo/Guidebook/Medical/Chemist.xml # Resources/ServerInfo/Guidebook/Medical/Medical.xml # Resources/ServerInfo/Guidebook/Medical/MedicalDoctor.xml # Resources/ServerInfo/Guidebook/Science/Science.xml # Resources/ServerInfo/Guidebook/Security/Security.xml # Resources/ServerInfo/Guidebook/Service/Bartender.xml # Resources/ServerInfo/Guidebook/Service/Chef.xml # Resources/ServerInfo/Guidebook/SpaceStation14.xml # Resources/ServerInfo/Guidebook/Survival.xml # Resources/Textures/Objects/Storage/boxes.rsi/meta.json
This commit is contained in:
commit
9888f8fa57
861 changed files with 34223 additions and 15182 deletions
|
|
@ -1,4 +1,5 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
|
||||
charset = utf-8
|
||||
|
|
@ -278,7 +279,7 @@ dotnet_naming_style.t_upper_camel_case_style.capitalization = pascal_case
|
|||
dotnet_naming_style.t_upper_camel_case_style.required_prefix = T
|
||||
dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||
dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.constants_symbols.applicable_kinds = field
|
||||
dotnet_naming_symbols.constants_symbols.required_modifiers = const
|
||||
|
||||
|
|
@ -317,20 +318,20 @@ dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static
|
|||
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static, readonly
|
||||
|
||||
dotnet_naming_symbols.property_symbols.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.property_symbols.applicable_kinds = property
|
||||
|
||||
dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||
dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field
|
||||
|
||||
dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||
dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field
|
||||
dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly
|
||||
dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static, readonly
|
||||
|
||||
dotnet_naming_symbols.types_and_namespaces_symbols.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace,class,struct,enum,delegate
|
||||
dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace, class, struct, enum, delegate
|
||||
|
||||
dotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter
|
||||
|
|
@ -342,6 +343,7 @@ resharper_csharp_wrap_parameters_style = chop_if_long
|
|||
resharper_keep_existing_attribute_arrangement = true
|
||||
resharper_wrap_chained_binary_patterns = chop_if_long
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
resharper_csharp_trailing_comma_in_multiline_lists = true
|
||||
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
||||
indent_size = 2
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Warps;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
|
|
@ -58,15 +56,20 @@ public class PvsBenchmark
|
|||
_pair.Server.CfgMan.SetCVar(CVars.NetPvsAsync, false);
|
||||
_sys = _entMan.System<SharedTransformSystem>();
|
||||
|
||||
SetupAsync().Wait();
|
||||
}
|
||||
|
||||
private async Task SetupAsync()
|
||||
{
|
||||
// Spawn the map
|
||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
_pair.Server.WaitPost(() =>
|
||||
await _pair.Server.WaitPost(() =>
|
||||
{
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
}).Wait();
|
||||
});
|
||||
|
||||
// Get list of ghost warp positions
|
||||
_spawns = _entMan.AllComponentsList<WarpPointComponent>()
|
||||
|
|
@ -76,17 +79,19 @@ public class PvsBenchmark
|
|||
|
||||
Array.Resize(ref _players, PlayerCount);
|
||||
|
||||
// Spawn "Players".
|
||||
_pair.Server.WaitPost(() =>
|
||||
// Spawn "Players"
|
||||
_players = await _pair.Server.AddDummySessions(PlayerCount);
|
||||
await _pair.Server.WaitPost(() =>
|
||||
{
|
||||
var mind = _pair.Server.System<MindSystem>();
|
||||
for (var i = 0; i < PlayerCount; i++)
|
||||
{
|
||||
var pos = _spawns[i % _spawns.Length];
|
||||
var uid =_entMan.SpawnEntity("MobHuman", pos);
|
||||
_pair.Server.ConsoleHost.ExecuteCommand($"setoutfit {_entMan.GetNetEntity(uid)} CaptainGear");
|
||||
_players[i] = new DummySession{AttachedEntity = uid};
|
||||
mind.ControlMob(_players[i].UserId, uid);
|
||||
}
|
||||
}).Wait();
|
||||
});
|
||||
|
||||
// Repeatedly move players around so that they "explore" the map and see lots of entities.
|
||||
// This will populate their PVS data with out-of-view entities.
|
||||
|
|
@ -168,20 +173,4 @@ public class PvsBenchmark
|
|||
}).Wait();
|
||||
_pair.Server.PvsTick(_players);
|
||||
}
|
||||
|
||||
private sealed class DummySession : ICommonSession
|
||||
{
|
||||
public SessionStatus Status => SessionStatus.InGame;
|
||||
public EntityUid? AttachedEntity {get; set; }
|
||||
public NetUserId UserId => default;
|
||||
public string Name => string.Empty;
|
||||
public short Ping => default;
|
||||
public INetChannel Channel { get; set; } = default!;
|
||||
public LoginType AuthType => default;
|
||||
public HashSet<EntityUid> ViewSubscriptions { get; } = new();
|
||||
public DateTime ConnectedTime { get; set; }
|
||||
public SessionState State => default!;
|
||||
public SessionData Data => default!;
|
||||
public bool ClientSide { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Access.UI
|
||||
{
|
||||
|
|
@ -40,7 +42,7 @@ namespace Content.Client.Access.UI
|
|||
SendMessage(new AgentIDCardJobChangedMessage(newJob));
|
||||
}
|
||||
|
||||
public void OnJobIconChanged(string newJobIconId)
|
||||
public void OnJobIconChanged(ProtoId<StatusIconPrototype> newJobIconId)
|
||||
{
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace Content.Client.Access.UI
|
|||
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
|
||||
}
|
||||
|
||||
public void SetAllowedIcons(HashSet<string> icons, string currentJobIconId)
|
||||
public void SetAllowedIcons(HashSet<ProtoId<StatusIconPrototype>> icons, string currentJobIconId)
|
||||
{
|
||||
IconGrid.DisposeAllChildren();
|
||||
|
||||
|
|
@ -46,10 +46,8 @@ namespace Content.Client.Access.UI
|
|||
var i = 0;
|
||||
foreach (var jobIconId in icons)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<StatusIconPrototype>(jobIconId, out var jobIcon))
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
|
||||
continue;
|
||||
}
|
||||
|
||||
String styleBase = StyleBase.ButtonOpenBoth;
|
||||
var modulo = i % JobIconColumnCount;
|
||||
|
|
@ -77,7 +75,7 @@ namespace Content.Client.Access.UI
|
|||
};
|
||||
|
||||
jobIconButton.AddChild(jobIconTexture);
|
||||
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID);
|
||||
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIconId);
|
||||
IconGrid.AddChild(jobIconButton);
|
||||
|
||||
if (jobIconId.Equals(currentJobIconId))
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
xmlns:tabs="clr-namespace:Content.Client.Administration.UI.Tabs"
|
||||
xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||
xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
|
||||
xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab">
|
||||
xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab"
|
||||
xmlns:baby="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab">
|
||||
<TabContainer Name="MasterTabContainer">
|
||||
<adminTab:AdminTab />
|
||||
<adminbusTab:AdminbusTab />
|
||||
|
|
@ -14,6 +15,7 @@
|
|||
<tabs:RoundTab />
|
||||
<tabs:ServerTab />
|
||||
<panic:PanicBunkerTab Name="PanicBunkerControl" Access="Public" />
|
||||
<baby:BabyJailTab Name="BabyJailControl" Access="Public" />
|
||||
<playerTab:PlayerTab Name="PlayerTabControl" Access="Public" />
|
||||
<objectsTab:ObjectsTab Name="ObjectsTabControl" Access="Public" />
|
||||
</TabContainer>
|
||||
|
|
|
|||
|
|
@ -21,8 +21,12 @@ namespace Content.Client.Administration.UI
|
|||
MasterTabContainer.SetTabTitle(3, Loc.GetString("admin-menu-round-tab"));
|
||||
MasterTabContainer.SetTabTitle(4, Loc.GetString("admin-menu-server-tab"));
|
||||
MasterTabContainer.SetTabTitle(5, Loc.GetString("admin-menu-panic-bunker-tab"));
|
||||
MasterTabContainer.SetTabTitle(6, Loc.GetString("admin-menu-players-tab"));
|
||||
MasterTabContainer.SetTabTitle(7, Loc.GetString("admin-menu-objects-tab"));
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
MasterTabContainer.SetTabTitle(6, Loc.GetString("admin-menu-baby-jail-tab"));
|
||||
MasterTabContainer.SetTabTitle(7, Loc.GetString("admin-menu-players-tab"));
|
||||
MasterTabContainer.SetTabTitle(8, Loc.GetString("admin-menu-objects-tab"));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
<controls:BabyJailStatusWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab"
|
||||
Title="{Loc admin-ui-baby-jail-window-title}">
|
||||
<RichTextLabel Name="MessageLabel" Access="Public" />
|
||||
</controls:BabyJailStatusWindow>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using Content.Client.Message;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.BabyJailTab;
|
||||
|
||||
/*
|
||||
* TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BabyJailStatusWindow : FancyWindow
|
||||
{
|
||||
public BabyJailStatusWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
MessageLabel.SetMarkup(Loc.GetString("admin-ui-baby-jail-is-enabled"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<controls:BabyJailTab
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Margin="4">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<cc:CommandButton Name="EnabledButton" Command="babyjail" ToggleMode="True"
|
||||
Text="{Loc admin-ui-baby-jail-disabled}"
|
||||
ToolTip="{Loc admin-ui-baby-jail-tooltip}" />
|
||||
<cc:CommandButton Name="ShowReasonButton" Command="babyjail_show_reason"
|
||||
ToggleMode="True" Text="{Loc admin-ui-baby-jail-show-reason}"
|
||||
ToolTip="{Loc admin-ui-baby-jail-show-reason-tooltip}" />
|
||||
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-baby-jail-max-account-age}" MinWidth="175" />
|
||||
<LineEdit Name="MaxAccountAge" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-baby-jail-max-overall-minutes}" MinWidth="175" />
|
||||
<LineEdit Name="MaxOverallMinutes" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:BabyJailTab>
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
using Content.Shared.Administration.Events;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
/*
|
||||
* TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.BabyJailTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BabyJailTab : Control
|
||||
{
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
|
||||
private string _maxAccountAge;
|
||||
private string _maxOverallMinutes;
|
||||
|
||||
public BabyJailTab()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
MaxAccountAge.OnTextEntered += args => SendMaxAccountAge(args.Text);
|
||||
MaxAccountAge.OnFocusExit += args => SendMaxAccountAge(args.Text);
|
||||
_maxAccountAge = MaxAccountAge.Text;
|
||||
|
||||
MaxOverallMinutes.OnTextEntered += args => SendMaxOverallMinutes(args.Text);
|
||||
MaxOverallMinutes.OnFocusExit += args => SendMaxOverallMinutes(args.Text);
|
||||
_maxOverallMinutes = MaxOverallMinutes.Text;
|
||||
}
|
||||
|
||||
private void SendMaxAccountAge(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text) ||
|
||||
text == _maxAccountAge ||
|
||||
!int.TryParse(text, out var minutes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ExecuteCommand($"babyjail_max_account_age {minutes}");
|
||||
}
|
||||
|
||||
private void SendMaxOverallMinutes(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text) ||
|
||||
text == _maxOverallMinutes ||
|
||||
!int.TryParse(text, out var minutes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ExecuteCommand($"babyjail_max_overall_minutes {minutes}");
|
||||
}
|
||||
|
||||
public void UpdateStatus(BabyJailStatus status)
|
||||
{
|
||||
EnabledButton.Pressed = status.Enabled;
|
||||
EnabledButton.Text = Loc.GetString(status.Enabled
|
||||
? "admin-ui-baby-jail-enabled"
|
||||
: "admin-ui-baby-jail-disabled"
|
||||
);
|
||||
EnabledButton.ModulateSelfOverride = status.Enabled ? Color.Red : null;
|
||||
ShowReasonButton.Pressed = status.ShowReason;
|
||||
|
||||
MaxAccountAge.Text = status.MaxAccountAgeMinutes.ToString();
|
||||
_maxAccountAge = MaxAccountAge.Text;
|
||||
|
||||
MaxOverallMinutes.Text = status.MaxOverallMinutes.ToString();
|
||||
_maxOverallMinutes = MaxOverallMinutes.Text;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,21 @@
|
|||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:ot="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
|
||||
xmlns:co="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label HorizontalExpand="True" SizeFlagsStretchRatio="0.50"
|
||||
Text="{Loc Object type:}" />
|
||||
<LineEdit Name="SearchLineEdit" PlaceHolder="{Loc Search...}" HorizontalExpand="True" SizeFlagsStretchRatio="1"/>
|
||||
<OptionButton Name="ObjectTypeOptions" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
</BoxContainer>
|
||||
<cc:HSeparator/>
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" Name="ObjectList">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
|
||||
<ot:ObjectsTabHeader Name="ListHeader"/>
|
||||
<cc:HSeparator/>
|
||||
<co:SearchListContainer Name="SearchList" Access="Public" VerticalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using Content.Client.Station;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
|
@ -10,20 +12,20 @@ namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
|||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTab : Control
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private readonly List<ObjectsTabEntry> _objects = new();
|
||||
private List<ObjectsTabSelection> _selections = new();
|
||||
private readonly List<ObjectsTabSelection> _selections = new();
|
||||
private bool _ascending = false; // Set to false for descending order by default
|
||||
private ObjectsTabHeader.Header _headerClicked = ObjectsTabHeader.Header.ObjectName;
|
||||
private readonly Color _altColor = Color.FromHex("#292B38");
|
||||
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
|
||||
|
||||
public event Action<ObjectsTabEntry, GUIBoundKeyEventArgs>? OnEntryKeyBindDown;
|
||||
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
||||
|
||||
// Listen I could either have like 4 different event subscribers (for map / grid / station changes) and manage their lifetimes in AdminUIController
|
||||
// OR
|
||||
// I can do this.
|
||||
private TimeSpan _updateFrequency = TimeSpan.FromSeconds(2);
|
||||
|
||||
private TimeSpan _nextUpdate = TimeSpan.FromSeconds(2);
|
||||
private readonly TimeSpan _updateFrequency = TimeSpan.FromSeconds(2);
|
||||
private TimeSpan _nextUpdate;
|
||||
|
||||
public ObjectsTab()
|
||||
{
|
||||
|
|
@ -42,6 +44,30 @@ public sealed partial class ObjectsTab : Control
|
|||
ObjectTypeOptions.AddItem(Enum.GetName((ObjectsTabSelection)type)!);
|
||||
}
|
||||
|
||||
ListHeader.OnHeaderClicked += HeaderClicked;
|
||||
SearchList.SearchBar = SearchLineEdit;
|
||||
SearchList.GenerateItem += GenerateButton;
|
||||
SearchList.DataFilterCondition += DataFilterCondition;
|
||||
|
||||
RefreshObjectList();
|
||||
// Set initial selection and refresh the list to apply the initial sort order
|
||||
var defaultSelection = ObjectsTabSelection.Grids;
|
||||
ObjectTypeOptions.SelectId((int)defaultSelection); // Set the default selection
|
||||
RefreshObjectList(defaultSelection); // Refresh the list with the default selection
|
||||
|
||||
// Initialize the next update time
|
||||
_nextUpdate = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_timing.CurTime < _nextUpdate)
|
||||
return;
|
||||
|
||||
_nextUpdate = _timing.CurTime + _updateFrequency;
|
||||
|
||||
RefreshObjectList();
|
||||
}
|
||||
|
||||
|
|
@ -81,32 +107,70 @@ public sealed partial class ObjectsTab : Control
|
|||
throw new ArgumentOutOfRangeException(nameof(selection), selection, null);
|
||||
}
|
||||
|
||||
foreach (var control in _objects)
|
||||
entities.Sort((a, b) =>
|
||||
{
|
||||
ObjectList.RemoveChild(control);
|
||||
var valueA = GetComparableValue(a, _headerClicked);
|
||||
var valueB = GetComparableValue(b, _headerClicked);
|
||||
return _ascending ? Comparer<object>.Default.Compare(valueA, valueB) : Comparer<object>.Default.Compare(valueB, valueA);
|
||||
});
|
||||
|
||||
var listData = new List<ObjectsListData>();
|
||||
for (int index = 0; index < entities.Count; index++)
|
||||
{
|
||||
var info = entities[index];
|
||||
listData.Add(new ObjectsListData(info, $"{info.Name} {info.Entity}", index % 2 == 0 ? _altColor : _defaultColor));
|
||||
}
|
||||
|
||||
_objects.Clear();
|
||||
|
||||
foreach (var (name, nent) in entities)
|
||||
{
|
||||
var ctrl = new ObjectsTabEntry(name, nent);
|
||||
_objects.Add(ctrl);
|
||||
ObjectList.AddChild(ctrl);
|
||||
ctrl.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(ctrl, args);
|
||||
}
|
||||
SearchList.PopulateList(listData);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_timing.CurTime < _nextUpdate)
|
||||
if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor })
|
||||
return;
|
||||
|
||||
// I do not care for precision.
|
||||
_nextUpdate = _timing.CurTime + _updateFrequency;
|
||||
var entry = new ObjectsTabEntry(info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor });
|
||||
button.ToolTip = $"{info.Name}, {info.Entity}";
|
||||
|
||||
button.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(args, data);
|
||||
button.AddChild(entry);
|
||||
}
|
||||
|
||||
private bool DataFilterCondition(string filter, ListData listData)
|
||||
{
|
||||
if (listData is not ObjectsListData { FilteringString: var filteringString })
|
||||
return false;
|
||||
|
||||
// If the filter is empty, do not filter out any entries
|
||||
if (string.IsNullOrEmpty(filter))
|
||||
return true;
|
||||
|
||||
return filteringString.Contains(filter, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private object GetComparableValue((string Name, NetEntity Entity) entity, ObjectsTabHeader.Header header)
|
||||
{
|
||||
return header switch
|
||||
{
|
||||
ObjectsTabHeader.Header.ObjectName => entity.Name,
|
||||
ObjectsTabHeader.Header.EntityID => entity.Entity.ToString(),
|
||||
_ => entity.Name
|
||||
};
|
||||
}
|
||||
|
||||
private void HeaderClicked(ObjectsTabHeader.Header header)
|
||||
{
|
||||
if (_headerClicked == header)
|
||||
{
|
||||
_ascending = !_ascending;
|
||||
}
|
||||
else
|
||||
{
|
||||
_headerClicked = header;
|
||||
_ascending = true;
|
||||
}
|
||||
|
||||
ListHeader.UpdateHeaderSymbols(_headerClicked, _ascending);
|
||||
RefreshObjectList();
|
||||
}
|
||||
|
||||
|
|
@ -118,3 +182,4 @@ public sealed partial class ObjectsTab : Control
|
|||
}
|
||||
}
|
||||
|
||||
public record ObjectsListData((string Name, NetEntity Entity) Info, string FilteringString, Color BackgroundColor) : ListData;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<ContainerButton xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<PanelContainer Name="BackgroundColorPanel"/>
|
||||
<PanelContainer xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Name="BackgroundColorPanel">
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
SeparationOverride="4">
|
||||
|
|
@ -14,4 +14,4 @@
|
|||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
</BoxContainer>
|
||||
</ContainerButton>
|
||||
</PanelContainer>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTabEntry : ContainerButton
|
||||
public sealed partial class ObjectsTabEntry : PanelContainer
|
||||
{
|
||||
public NetEntity AssocEntity;
|
||||
|
||||
public ObjectsTabEntry(string name, NetEntity nent)
|
||||
public ObjectsTabEntry(string name, NetEntity nent, StyleBox styleBox)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
AssocEntity = nent;
|
||||
EIDLabel.Text = nent.ToString();
|
||||
NameLabel.Text = name;
|
||||
BackgroundColorPanel.PanelOverride = styleBox;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<PanelContainer Name="BackgroundColorPanel" Access="Public"/>
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
SeparationOverride="4">
|
||||
<Label Name="ObjectNameLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc object-tab-object-name}"
|
||||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<Label Name="EntityIDLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc object-tab-entity-id}"
|
||||
MouseFilter="Pass"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Input;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTabHeader : Control
|
||||
{
|
||||
public event Action<Header>? OnHeaderClicked;
|
||||
|
||||
private const string ArrowUp = "↑";
|
||||
private const string ArrowDown = "↓";
|
||||
|
||||
public ObjectsTabHeader()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
ObjectNameLabel.OnKeyBindDown += ObjectNameClicked;
|
||||
EntityIDLabel.OnKeyBindDown += EntityIDClicked;
|
||||
}
|
||||
|
||||
public Label GetHeader(Header header)
|
||||
{
|
||||
return header switch
|
||||
{
|
||||
Header.ObjectName => ObjectNameLabel,
|
||||
Header.EntityID => EntityIDLabel,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(header), header, null)
|
||||
};
|
||||
}
|
||||
|
||||
public void ResetHeaderText()
|
||||
{
|
||||
ObjectNameLabel.Text = Loc.GetString("object-tab-object-name");
|
||||
EntityIDLabel.Text = Loc.GetString("object-tab-entity-id");
|
||||
}
|
||||
|
||||
public void UpdateHeaderSymbols(Header headerClicked, bool ascending)
|
||||
{
|
||||
ResetHeaderText();
|
||||
var arrow = ascending ? ArrowUp : ArrowDown;
|
||||
GetHeader(headerClicked).Text += $" {arrow}";
|
||||
}
|
||||
|
||||
private void HeaderClicked(GUIBoundKeyEventArgs args, Header header)
|
||||
{
|
||||
if (args.Function != EngineKeyFunctions.UIClick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnHeaderClicked?.Invoke(header);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
private void ObjectNameClicked(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
HeaderClicked(args, Header.ObjectName);
|
||||
}
|
||||
|
||||
private void EntityIDClicked(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
HeaderClicked(args, Header.EntityID);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
ObjectNameLabel.OnKeyBindDown -= ObjectNameClicked;
|
||||
EntityIDLabel.OnKeyBindDown -= EntityIDClicked;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Header
|
||||
{
|
||||
ObjectName,
|
||||
EntityID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,12 +31,12 @@
|
|||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-panic-bunker-min-account-age}" MinWidth="175" />
|
||||
<LineEdit Name="MinAccountAge" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-hours}" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-panic-bunker-min-overall-hours}" MinWidth="175" />
|
||||
<LineEdit Name="MinOverallHours" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-hours}" />
|
||||
<Label Text="{Loc admin-ui-panic-bunker-min-overall-minutes}" MinWidth="175" />
|
||||
<LineEdit Name="MinOverallMinutes" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
</BoxContainer>
|
||||
<!-- Corvax-VPNGuard-Start -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="2" Visible="False" Name="VPNContainer">
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public sealed partial class PanicBunkerTab : Control
|
|||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
|
||||
private string _minAccountAge;
|
||||
private string _minOverallHours;
|
||||
private string _minOverallMinutes;
|
||||
|
||||
public PanicBunkerTab()
|
||||
{
|
||||
|
|
@ -26,9 +26,9 @@ public sealed partial class PanicBunkerTab : Control
|
|||
MinAccountAge.OnFocusExit += args => SendMinAccountAge(args.Text);
|
||||
_minAccountAge = MinAccountAge.Text;
|
||||
|
||||
MinOverallHours.OnTextEntered += args => SendMinOverallHours(args.Text);
|
||||
MinOverallHours.OnFocusExit += args => SendMinOverallHours(args.Text);
|
||||
_minOverallHours = MinOverallHours.Text;
|
||||
MinOverallMinutes.OnTextEntered += args => SendMinOverallMinutes(args.Text);
|
||||
MinOverallMinutes.OnFocusExit += args => SendMinOverallMinutes(args.Text);
|
||||
_minOverallMinutes = MinOverallMinutes.Text;
|
||||
// Corvax-VPNGuard-Start
|
||||
var haveSecrets = IoCManager.Instance!.TryResolveType<ISharedSponsorsManager>(out _); // TODO: Probably need better way to detect Secrets module
|
||||
if (haveSecrets)
|
||||
|
|
@ -51,16 +51,16 @@ public sealed partial class PanicBunkerTab : Control
|
|||
_console.ExecuteCommand($"panicbunker_min_account_age {minutes}");
|
||||
}
|
||||
|
||||
private void SendMinOverallHours(string text)
|
||||
private void SendMinOverallMinutes(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text) ||
|
||||
text == _minOverallHours ||
|
||||
!int.TryParse(text, out var hours))
|
||||
text == _minOverallMinutes ||
|
||||
!int.TryParse(text, out var minutes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ExecuteCommand($"panicbunker_min_overall_hours {hours}");
|
||||
_console.ExecuteCommand($"panicbunker_min_overall_minutes {minutes}");
|
||||
}
|
||||
|
||||
// Corvax-VPNGuard-Start
|
||||
|
|
@ -84,11 +84,11 @@ public sealed partial class PanicBunkerTab : Control
|
|||
CountDeadminnedButton.Pressed = status.CountDeadminnedAdmins;
|
||||
ShowReasonButton.Pressed = status.ShowReason;
|
||||
|
||||
MinAccountAge.Text = status.MinAccountAgeHours.ToString();
|
||||
MinAccountAge.Text = status.MinAccountAgeMinutes.ToString();
|
||||
_minAccountAge = MinAccountAge.Text;
|
||||
|
||||
MinOverallHours.Text = status.MinOverallHours.ToString();
|
||||
_minOverallHours = MinOverallHours.Text;
|
||||
MinOverallMinutes.Text = status.MinOverallMinutes.ToString();
|
||||
_minOverallMinutes = MinOverallMinutes.Text;
|
||||
DenyVPN.Pressed = status.DenyVpn; // Corvax-VPNGuard
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
|||
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
|
||||
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
|
||||
}
|
||||
|
||||
#region Antag Overlay
|
||||
|
|
@ -110,7 +111,9 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
|||
_players = players;
|
||||
PlayerCount.Text = $"Players: {_playerMan.PlayerCount}";
|
||||
|
||||
var sortedPlayers = new List<PlayerInfo>(players);
|
||||
var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
|
||||
|
||||
var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
|
||||
sortedPlayers.Sort(Compare);
|
||||
|
||||
UpdateHeaderSymbols();
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
using Content.Shared.Antag;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Antag;
|
||||
|
||||
/// <summary>
|
||||
/// Used for assigning specified icons for antags.
|
||||
/// </summary>
|
||||
public sealed class AntagStatusIconSystem : SharedStatusIconSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
|
||||
SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(GetIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetIcon);
|
||||
SubscribeLocalEvent<InitialInfectedComponent, GetStatusIconsEvent>(GetIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Status Icon on an entity if the player is supposed to see it.
|
||||
/// </summary>
|
||||
private void GetIcon<T>(EntityUid uid, T comp, ref GetStatusIconsEvent ev) where T: IAntagStatusIconComponent
|
||||
{
|
||||
var ent = _player.LocalSession?.AttachedEntity;
|
||||
|
||||
var canEv = new CanDisplayStatusIconsEvent(ent);
|
||||
RaiseLocalEvent(uid, ref canEv);
|
||||
|
||||
if (!canEv.Cancelled)
|
||||
ev.StatusIcons.Add(_prototype.Index(comp.StatusIcon));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Rev Icon on an entity if the player is supposed to see it. This additional function is needed to deal
|
||||
/// with a special case where if someone is a head rev we only want to display the headrev icon.
|
||||
/// </summary>
|
||||
private void GetRevIcon(EntityUid uid, RevolutionaryComponent comp, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(uid))
|
||||
return;
|
||||
|
||||
GetIcon(uid, comp, ref ev);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
using Content.Server.Bed.Sleep;
|
||||
|
||||
namespace Content.Client.Bed;
|
||||
|
||||
public sealed class SleepingSystem : SharedSleepingSystem
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using Content.Shared.Chemistry;
|
|||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Rounding;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -150,6 +151,9 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
|||
if (!TryComp(uid, out AppearanceComponent? appearance))
|
||||
return;
|
||||
|
||||
if (!TryComp<ItemComponent>(uid, out var item))
|
||||
return;
|
||||
|
||||
if (!AppearanceSystem.TryGetData<float>(uid, SolutionContainerVisuals.FillFraction, out var fraction, appearance))
|
||||
return;
|
||||
|
||||
|
|
@ -159,7 +163,8 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
|||
{
|
||||
var layer = new PrototypeLayerData();
|
||||
|
||||
var key = "inhand-" + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite;
|
||||
var heldPrefix = item.HeldPrefix == null ? "inhand-" : $"{item.HeldPrefix}-inhand-";
|
||||
var key = heldPrefix + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite;
|
||||
|
||||
layer.State = key;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public sealed class ShowHealthBarsCommand : LocalizedCommands
|
|||
var showHealthBarsComponent = new ShowHealthBarsComponent
|
||||
{
|
||||
DamageContainers = args.ToList(),
|
||||
HealthStatusIcon = null,
|
||||
NetSyncEnabled = false
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ namespace Content.Client.Construction
|
|||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
|
|
@ -195,9 +196,8 @@ namespace Content.Client.Construction
|
|||
if (GhostPresent(loc))
|
||||
return false;
|
||||
|
||||
// This InRangeUnobstructed should probably be replaced with "is there something blocking us in that tile?"
|
||||
var predicate = GetPredicate(prototype.CanBuildInImpassable, loc.ToMap(EntityManager, _transformSystem));
|
||||
if (!_interactionSystem.InRangeUnobstructed(user, loc, 20f, predicate: predicate))
|
||||
if (!_examineSystem.InRangeUnOccluded(user, loc, 20f, predicate: predicate))
|
||||
return false;
|
||||
|
||||
if (!CheckConstructionConditions(prototype, loc, dir, user, showPopup: true))
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True" Margin="10">
|
||||
<BoxContainer SizeFlagsStretchRatio="2" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<SpriteView Name="MachineSprite" Scale="4 4" HorizontalAlignment="Center" VerticalExpand="True" MinSize="128 128"/>
|
||||
<EntityPrototypeView Name="MachineSprite" Scale="4 4" HorizontalAlignment="Center" VerticalExpand="True" MinSize="128 128"/>
|
||||
<RichTextLabel Name="MachineNameLabel" HorizontalAlignment="Center" StyleClasses="LabelKeyText"/>
|
||||
</BoxContainer>
|
||||
<Control MinHeight="10"/>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
private readonly ItemSlotsSystem _itemSlots;
|
||||
private readonly FlatpackSystem _flatpack;
|
||||
private readonly MaterialStorageSystem _materialStorage;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
|
||||
private readonly EntityUid _owner;
|
||||
|
||||
|
|
@ -31,7 +30,6 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
public const string NoBoardEffectId = "FlatpackerNoBoardEffect";
|
||||
|
||||
private EntityUid? _currentBoard = EntityUid.Invalid;
|
||||
private EntityUid? _machinePreview;
|
||||
|
||||
public event Action? PackButtonPressed;
|
||||
|
||||
|
|
@ -43,7 +41,6 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
_itemSlots = _entityManager.System<ItemSlotsSystem>();
|
||||
_flatpack = _entityManager.System<FlatpackSystem>();
|
||||
_materialStorage = _entityManager.System<MaterialStorageSystem>();
|
||||
_spriteSystem = _entityManager.System<SpriteSystem>();
|
||||
|
||||
_owner = uid;
|
||||
|
||||
|
|
@ -57,17 +54,10 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_machinePreview is not { } && _entityManager.Deleted(_machinePreview))
|
||||
{
|
||||
_machinePreview = null;
|
||||
MachineSprite.SetEntity(_machinePreview);
|
||||
}
|
||||
|
||||
if (!_entityManager.TryGetComponent<FlatpackCreatorComponent>(_owner, out var flatpacker) ||
|
||||
!_itemSlots.TryGetSlot(_owner, flatpacker.SlotId, out var itemSlot))
|
||||
return;
|
||||
|
||||
MachineBoardComponent? machineBoardComp = null;
|
||||
if (flatpacker.Packing)
|
||||
{
|
||||
PackButton.Disabled = true;
|
||||
|
|
@ -75,11 +65,10 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
else if (_currentBoard != null)
|
||||
{
|
||||
Dictionary<string, int> cost;
|
||||
if (_entityManager.TryGetComponent(_currentBoard, out machineBoardComp) &&
|
||||
machineBoardComp.Prototype is not null)
|
||||
if (_entityManager.TryGetComponent<MachineBoardComponent>(_currentBoard, out var machineBoardComp))
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoardComp));
|
||||
else
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker));
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), null);
|
||||
|
||||
PackButton.Disabled = !_materialStorage.CanChangeMaterialAmount(_owner, cost);
|
||||
}
|
||||
|
|
@ -87,9 +76,6 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
if (_currentBoard == itemSlot.Item)
|
||||
return;
|
||||
|
||||
if (_machinePreview != null)
|
||||
_entityManager.DeleteEntity(_machinePreview);
|
||||
|
||||
_currentBoard = itemSlot.Item;
|
||||
CostHeaderLabel.Visible = _currentBoard != null;
|
||||
InsertLabel.Visible = _currentBoard == null;
|
||||
|
|
@ -99,35 +85,32 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
string? prototype = null;
|
||||
Dictionary<string, int>? cost = null;
|
||||
|
||||
if (machineBoardComp != null || _entityManager.TryGetComponent(_currentBoard, out machineBoardComp))
|
||||
if (_entityManager.TryGetComponent<MachineBoardComponent>(_currentBoard, out var newMachineBoardComp))
|
||||
{
|
||||
prototype = machineBoardComp.Prototype;
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoardComp));
|
||||
prototype = newMachineBoardComp.Prototype;
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, newMachineBoardComp));
|
||||
}
|
||||
else if (_entityManager.TryGetComponent<ComputerBoardComponent>(_currentBoard, out var computerBoard))
|
||||
{
|
||||
prototype = computerBoard.Prototype;
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker));
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), null);
|
||||
}
|
||||
|
||||
if (prototype is not null && cost is not null)
|
||||
{
|
||||
var proto = _prototypeManager.Index<EntityPrototype>(prototype);
|
||||
_machinePreview = _entityManager.Spawn(proto.ID);
|
||||
_spriteSystem.ForceUpdate(_machinePreview.Value);
|
||||
MachineSprite.SetPrototype(prototype);
|
||||
MachineNameLabel.SetMessage(proto.Name);
|
||||
CostLabel.SetMarkup(GetCostString(cost));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_machinePreview = _entityManager.Spawn(NoBoardEffectId);
|
||||
MachineSprite.SetPrototype(NoBoardEffectId);
|
||||
CostLabel.SetMessage(Loc.GetString("flatpacker-ui-no-board-label"));
|
||||
MachineNameLabel.SetMessage(" ");
|
||||
PackButton.Disabled = true;
|
||||
}
|
||||
|
||||
MachineSprite.SetEntity(_machinePreview);
|
||||
}
|
||||
|
||||
private string GetCostString(Dictionary<string, int> costs)
|
||||
|
|
@ -149,7 +132,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
("amount", amountText),
|
||||
("material", Loc.GetString(matProto.Name)));
|
||||
|
||||
msg.AddMarkup(text);
|
||||
msg.TryAddMarkup(text, out _);
|
||||
|
||||
if (i != orderedCosts.Length - 1)
|
||||
msg.PushNewline();
|
||||
|
|
@ -157,12 +140,4 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
|||
|
||||
return msg.ToMarkup();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
|
||||
_entityManager.DeleteEntity(_machinePreview);
|
||||
_machinePreview = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@ using Content.Client.Chat.Managers;
|
|||
using Content.Client.Corvax.TTS;
|
||||
using Content.Client.Options;
|
||||
using Content.Client.Eui;
|
||||
using Content.Client.Flash;
|
||||
using Content.Client.Fullscreen;
|
||||
using Content.Client.GhostKick;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Input;
|
||||
using Content.Client.IoC;
|
||||
using Content.Client.Launcher;
|
||||
|
|
@ -54,7 +52,6 @@ namespace Content.Client.Entry
|
|||
[Dependency] private readonly IScreenshotHook _screenshotHook = default!;
|
||||
[Dependency] private readonly FullscreenHook _fullscreenHook = default!;
|
||||
[Dependency] private readonly ChangelogManager _changelogManager = default!;
|
||||
[Dependency] private readonly RulesManager _rulesManager = default!;
|
||||
[Dependency] private readonly ViewportManager _viewportManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
|
@ -128,7 +125,6 @@ namespace Content.Client.Entry
|
|||
_screenshotHook.Initialize();
|
||||
_fullscreenHook.Initialize();
|
||||
_changelogManager.Initialize();
|
||||
_rulesManager.Initialize();
|
||||
_viewportManager.Initialize();
|
||||
_ghostKick.Initialize();
|
||||
_extendedDisconnectInformation.Initialize();
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ using Content.Client.Lobby;
|
|||
using Content.Client.RoundEnd;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.GameWindow;
|
||||
using Content.Shared.Roles;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.GameTicking.Managers
|
||||
{
|
||||
|
|
@ -17,10 +19,9 @@ namespace Content.Client.GameTicking.Managers
|
|||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IClientAdminManager _admin = default!;
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
|
||||
private Dictionary<NetEntity, Dictionary<string, uint?>> _jobsAvailable = new();
|
||||
private Dictionary<NetEntity, Dictionary<ProtoId<JobPrototype>, int?>> _jobsAvailable = new();
|
||||
private Dictionary<NetEntity, string> _stationNames = new();
|
||||
|
||||
[ViewVariables] public bool AreWeReady { get; private set; }
|
||||
|
|
@ -32,13 +33,13 @@ namespace Content.Client.GameTicking.Managers
|
|||
[ViewVariables] public TimeSpan StartTime { get; private set; }
|
||||
[ViewVariables] public new bool Paused { get; private set; }
|
||||
|
||||
[ViewVariables] public IReadOnlyDictionary<NetEntity, Dictionary<string, uint?>> JobsAvailable => _jobsAvailable;
|
||||
[ViewVariables] public IReadOnlyDictionary<NetEntity, Dictionary<ProtoId<JobPrototype>, int?>> JobsAvailable => _jobsAvailable;
|
||||
[ViewVariables] public IReadOnlyDictionary<NetEntity, string> StationNames => _stationNames;
|
||||
|
||||
public event Action? InfoBlobUpdated;
|
||||
public event Action? LobbyStatusUpdated;
|
||||
public event Action? LobbyLateJoinStatusUpdated;
|
||||
public event Action<IReadOnlyDictionary<NetEntity, Dictionary<string, uint?>>>? LobbyJobsAvailableUpdated;
|
||||
public event Action<IReadOnlyDictionary<NetEntity, Dictionary<ProtoId<JobPrototype>, int?>>>? LobbyJobsAvailableUpdated;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -69,7 +70,7 @@ namespace Content.Client.GameTicking.Managers
|
|||
// reading the console. E.g., logs like this one could leak the nuke station/grid:
|
||||
// > Grid NT-Arrivals 1101 (122/n25896) changed parent. Old parent: map 10 (121/n25895). New parent: FTL (123/n26470)
|
||||
#if !DEBUG
|
||||
_map.Log.Level = _admin.IsAdmin() ? LogLevel.Info : LogLevel.Warning;
|
||||
EntityManager.System<SharedMapSystem>().Log.Level = _admin.IsAdmin() ? LogLevel.Info : LogLevel.Warning;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
using Content.Shared.Guidebook;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Guidebook.Components;
|
||||
|
||||
|
|
@ -13,9 +14,8 @@ public sealed partial class GuideHelpComponent : Component
|
|||
/// What guides to include show when opening the guidebook. The first entry will be used to select the currently
|
||||
/// selected guidebook.
|
||||
/// </summary>
|
||||
[DataField("guides", customTypeSerializer: typeof(PrototypeIdListSerializer<GuideEntryPrototype>), required: true)]
|
||||
[ViewVariables]
|
||||
public List<string> Guides = new();
|
||||
[DataField(required: true)]
|
||||
public List<ProtoId<GuideEntryPrototype>> Guides = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to automatically include the children of the given guides.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:fancyTree="clr-namespace:Content.Client.UserInterface.Controls.FancyTree"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
SetSize="750 700"
|
||||
SetSize="900 700"
|
||||
MinSize="100 200"
|
||||
Resizable="True"
|
||||
Title="{Loc 'guidebook-window-title'}">
|
||||
|
|
@ -18,12 +18,16 @@
|
|||
Name="SearchBar"
|
||||
PlaceHolder="{Loc 'guidebook-filter-placeholder-text'}"
|
||||
HorizontalExpand="True"
|
||||
Margin="0 5 10 5">
|
||||
Margin="0 5 10 5">
|
||||
</LineEdit>
|
||||
</BoxContainer>
|
||||
<BoxContainer Access="Internal" Name="ReturnContainer" Orientation="Horizontal" HorizontalAlignment="Right" Visible="False">
|
||||
<Button Name="HomeButton" Text="{Loc 'ui-rules-button-home'}" Margin="0 0 10 0"/>
|
||||
</BoxContainer>
|
||||
<ScrollContainer Name="Scroll" HScrollEnabled="False" HorizontalExpand="True" VerticalExpand="True">
|
||||
<Control>
|
||||
<BoxContainer Orientation="Vertical" Name="EntryContainer" Margin="5 5 5 5" Visible="False"/>
|
||||
<BoxContainer Orientation="Vertical" Name="EntryContainer" Margin="5 5 5 5" Visible="False">
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" Name="Placeholder" Margin="5 5 5 5">
|
||||
<Label HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Loc 'guidebook-placeholder-text'}"/>
|
||||
<Label HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Loc 'guidebook-placeholder-text-2'}"/>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Client.Guidebook.RichText;
|
||||
using Content.Client.UserInterface.ControlExtensions;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.UserInterface.Controls.FancyTree;
|
||||
using JetBrains.Annotations;
|
||||
using Content.Client.UserInterface.Systems.Info;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Guidebook;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Guidebook.Controls;
|
||||
|
||||
|
|
@ -19,7 +21,7 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
|
|||
[Dependency] private readonly IResourceManager _resourceManager = default!;
|
||||
[Dependency] private readonly DocumentParsingManager _parsingMan = default!;
|
||||
|
||||
private Dictionary<string, GuideEntry> _entries = new();
|
||||
private Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry> _entries = new();
|
||||
|
||||
public GuidebookWindow()
|
||||
{
|
||||
|
|
@ -37,7 +39,13 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
|
|||
private void OnSelectionChanged(TreeItem? item)
|
||||
{
|
||||
if (item != null && item.Metadata is GuideEntry entry)
|
||||
{
|
||||
ShowGuide(entry);
|
||||
|
||||
var isRulesEntry = entry.RuleEntry;
|
||||
ReturnContainer.Visible = isRulesEntry;
|
||||
HomeButton.OnPressed += _ => ShowGuide(entry);
|
||||
}
|
||||
else
|
||||
ClearSelectedGuide();
|
||||
}
|
||||
|
|
@ -69,10 +77,10 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
|
|||
}
|
||||
|
||||
public void UpdateGuides(
|
||||
Dictionary<string, GuideEntry> entries,
|
||||
List<string>? rootEntries = null,
|
||||
string? forceRoot = null,
|
||||
string? selected = null)
|
||||
Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry> entries,
|
||||
List<ProtoId<GuideEntryPrototype>>? rootEntries = null,
|
||||
ProtoId<GuideEntryPrototype>? forceRoot = null,
|
||||
ProtoId<GuideEntryPrototype>? selected = null)
|
||||
{
|
||||
_entries = entries;
|
||||
RepopulateTree(rootEntries, forceRoot);
|
||||
|
|
@ -98,11 +106,11 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
|
|||
}
|
||||
}
|
||||
|
||||
private IEnumerable<GuideEntry> GetSortedEntries(List<string>? rootEntries)
|
||||
private IEnumerable<GuideEntry> GetSortedEntries(List<ProtoId<GuideEntryPrototype>>? rootEntries)
|
||||
{
|
||||
if (rootEntries == null)
|
||||
{
|
||||
HashSet<string> entries = new(_entries.Keys);
|
||||
HashSet<ProtoId<GuideEntryPrototype>> entries = new(_entries.Keys);
|
||||
foreach (var entry in _entries.Values)
|
||||
{
|
||||
if (entry.Children.Count > 0)
|
||||
|
|
@ -111,7 +119,7 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
|
|||
.Select(childId => _entries[childId])
|
||||
.OrderBy(childEntry => childEntry.Priority)
|
||||
.ThenBy(childEntry => Loc.GetString(childEntry.Name))
|
||||
.Select(childEntry => childEntry.Id)
|
||||
.Select(childEntry => new ProtoId<GuideEntryPrototype>(childEntry.Id))
|
||||
.ToList();
|
||||
|
||||
entry.Children = sortedChildren;
|
||||
|
|
@ -127,13 +135,13 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
|
|||
.ThenBy(rootEntry => Loc.GetString(rootEntry.Name));
|
||||
}
|
||||
|
||||
private void RepopulateTree(List<string>? roots = null, string? forcedRoot = null)
|
||||
private void RepopulateTree(List<ProtoId<GuideEntryPrototype>>? roots = null, ProtoId<GuideEntryPrototype>? forcedRoot = null)
|
||||
{
|
||||
Tree.Clear();
|
||||
|
||||
HashSet<string> addedEntries = new();
|
||||
HashSet<ProtoId<GuideEntryPrototype>> addedEntries = new();
|
||||
|
||||
TreeItem? parent = forcedRoot == null ? null : AddEntry(forcedRoot, null, addedEntries);
|
||||
TreeItem? parent = forcedRoot == null ? null : AddEntry(forcedRoot.Value, null, addedEntries);
|
||||
foreach (var entry in GetSortedEntries(roots))
|
||||
{
|
||||
AddEntry(entry.Id, parent, addedEntries);
|
||||
|
|
@ -141,17 +149,23 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
|
|||
Tree.SetAllExpanded(true);
|
||||
}
|
||||
|
||||
private TreeItem? AddEntry(string id, TreeItem? parent, HashSet<string> addedEntries)
|
||||
private TreeItem? AddEntry(ProtoId<GuideEntryPrototype> id, TreeItem? parent, HashSet<ProtoId<GuideEntryPrototype>> addedEntries)
|
||||
{
|
||||
if (!_entries.TryGetValue(id, out var entry))
|
||||
return null;
|
||||
|
||||
if (!addedEntries.Add(id))
|
||||
{
|
||||
// TODO GUIDEBOOK Maybe allow duplicate entries?
|
||||
// E.g., for adding medicine under both chemicals & the chemist job
|
||||
Logger.Error($"Adding duplicate guide entry: {id}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var rulesProto = UserInterfaceManager.GetUIController<InfoUIController>().GetCoreRuleEntry();
|
||||
if (entry.RuleEntry && entry.Id != rulesProto.Id)
|
||||
return null;
|
||||
|
||||
var item = Tree.AddItem(parent);
|
||||
item.Metadata = entry;
|
||||
var name = Loc.GetString(entry.Name);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
using System.Linq;
|
||||
using Content.Client.Guidebook.Richtext;
|
||||
using Content.Shared.Guidebook;
|
||||
using Pidgin;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Sandboxing;
|
||||
using static Pidgin.Parser;
|
||||
|
|
@ -13,7 +16,9 @@ namespace Content.Client.Guidebook;
|
|||
/// </summary>
|
||||
public sealed partial class DocumentParsingManager
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IResourceManager _resourceManager = default!;
|
||||
[Dependency] private readonly ISandboxHelper _sandboxHelper = default!;
|
||||
|
||||
private readonly Dictionary<string, Parser<char, Control>> _tagControlParsers = new();
|
||||
|
|
@ -37,6 +42,21 @@ public sealed partial class DocumentParsingManager
|
|||
ControlParser = SkipWhitespaces.Then(_controlParser.Many());
|
||||
}
|
||||
|
||||
public bool TryAddMarkup(Control control, ProtoId<GuideEntryPrototype> entryId, bool log = true)
|
||||
{
|
||||
if (!_prototype.TryIndex(entryId, out var entry))
|
||||
return false;
|
||||
|
||||
using var file = _resourceManager.ContentFileReadText(entry.Text);
|
||||
return TryAddMarkup(control, file.ReadToEnd(), log);
|
||||
}
|
||||
|
||||
public bool TryAddMarkup(Control control, GuideEntry entry, bool log = true)
|
||||
{
|
||||
using var file = _resourceManager.ContentFileReadText(entry.Text);
|
||||
return TryAddMarkup(control, file.ReadToEnd(), log);
|
||||
}
|
||||
|
||||
public bool TryAddMarkup(Control control, string text, bool log = true)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System.Linq;
|
|||
using Content.Client.Guidebook.Components;
|
||||
using Content.Client.Light;
|
||||
using Content.Client.Verbs;
|
||||
using Content.Shared.Guidebook;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Light.Components;
|
||||
using Content.Shared.Speech;
|
||||
|
|
@ -13,6 +14,7 @@ using Robust.Shared.Audio;
|
|||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
|
@ -31,7 +33,12 @@ public sealed class GuidebookSystem : EntitySystem
|
|||
[Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!;
|
||||
[Dependency] private readonly TagSystem _tags = default!;
|
||||
|
||||
public event Action<List<string>, List<string>?, string?, bool, string?>? OnGuidebookOpen;
|
||||
public event Action<List<ProtoId<GuideEntryPrototype>>,
|
||||
List<ProtoId<GuideEntryPrototype>>?,
|
||||
ProtoId<GuideEntryPrototype>?,
|
||||
bool,
|
||||
ProtoId<GuideEntryPrototype>?>? OnGuidebookOpen;
|
||||
|
||||
public const string GuideEmbedTag = "GuideEmbeded";
|
||||
|
||||
private EntityUid _defaultUser;
|
||||
|
|
@ -80,7 +87,7 @@ public sealed class GuidebookSystem : EntitySystem
|
|||
});
|
||||
}
|
||||
|
||||
public void OpenHelp(List<string> guides)
|
||||
public void OpenHelp(List<ProtoId<GuideEntryPrototype>> guides)
|
||||
{
|
||||
OnGuidebookOpen?.Invoke(guides, null, null, true, guides[0]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
using Content.Shared.Info;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Content.Client.Info;
|
||||
|
||||
public sealed class InfoSystem : EntitySystem
|
||||
{
|
||||
public RulesMessage Rules = new RulesMessage("Server Rules", "The server did not send any rules.");
|
||||
[Dependency] private readonly RulesManager _rules = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeNetworkEvent<RulesMessage>(OnRulesReceived);
|
||||
Log.Debug("Requested server info.");
|
||||
RaiseNetworkEvent(new RequestRulesMessage());
|
||||
}
|
||||
|
||||
private void OnRulesReceived(RulesMessage message, EntitySessionEventArgs eventArgs)
|
||||
{
|
||||
Log.Debug("Received server rules.");
|
||||
Rules = message;
|
||||
_rules.UpdateRules();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Systems.EscapeMenu;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
|
||||
namespace Content.Client.Info
|
||||
|
|
@ -12,7 +10,6 @@ namespace Content.Client.Info
|
|||
public sealed class RulesAndInfoWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IResourceManager _resourceManager = default!;
|
||||
[Dependency] private readonly RulesManager _rules = default!;
|
||||
|
||||
public RulesAndInfoWindow()
|
||||
{
|
||||
|
|
@ -22,8 +19,14 @@ namespace Content.Client.Info
|
|||
|
||||
var rootContainer = new TabContainer();
|
||||
|
||||
var rulesList = new Info();
|
||||
var tutorialList = new Info();
|
||||
var rulesList = new RulesControl
|
||||
{
|
||||
Margin = new Thickness(10)
|
||||
};
|
||||
var tutorialList = new Info
|
||||
{
|
||||
Margin = new Thickness(10)
|
||||
};
|
||||
|
||||
rootContainer.AddChild(rulesList);
|
||||
rootContainer.AddChild(tutorialList);
|
||||
|
|
@ -31,7 +34,6 @@ namespace Content.Client.Info
|
|||
TabContainer.SetTabTitle(rulesList, Loc.GetString("ui-info-tab-rules"));
|
||||
TabContainer.SetTabTitle(tutorialList, Loc.GetString("ui-info-tab-tutorial"));
|
||||
|
||||
AddSection(rulesList, _rules.RulesSection());
|
||||
PopulateTutorial(tutorialList);
|
||||
|
||||
Contents.AddChild(rootContainer);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,18 @@
|
|||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
Name="InfoContainer"
|
||||
Orientation="Vertical"
|
||||
Margin="2 2 0 0"
|
||||
SeparationOverride="10"
|
||||
Access="Public"/>
|
||||
<BoxContainer xmlns="https://spacestation14.io" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
|
||||
<Control HorizontalExpand="True" VerticalExpand="True" HorizontalAlignment="Stretch">
|
||||
<ScrollContainer Name="Scroll" HScrollEnabled="False" HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Name="RulesContainer" VerticalExpand="True" Margin="0 0 5 0"/>
|
||||
</ScrollContainer>
|
||||
<BoxContainer Margin="0 0 15 0" HorizontalExpand="True" HorizontalAlignment="Right">
|
||||
<Button Name="BackButton"
|
||||
Text="{Loc 'ui-rules-button-back'}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"/>
|
||||
<Control MinWidth="5"/>
|
||||
<Button Name="HomeButton"
|
||||
Text="{Loc 'ui-rules-button-home'}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
</BoxContainer>
|
||||
|
|
|
|||
|
|
@ -1,22 +1,54 @@
|
|||
using System.IO;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Guidebook.RichText;
|
||||
using Content.Client.UserInterface.Systems.Info;
|
||||
using Content.Shared.Guidebook;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Info;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RulesControl : BoxContainer
|
||||
public sealed partial class RulesControl : BoxContainer, ILinkClickHandler
|
||||
{
|
||||
[Dependency] private readonly RulesManager _rules = default!;
|
||||
[Dependency] private readonly DocumentParsingManager _parsingMan = default!;
|
||||
|
||||
private string? _currentEntry;
|
||||
private readonly Stack<string> _priorEntries = new();
|
||||
|
||||
public RulesControl()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
AddChild(_rules.RulesSection());
|
||||
|
||||
SetGuide();
|
||||
|
||||
HomeButton.OnPressed += _ => SetGuide();
|
||||
|
||||
BackButton.OnPressed += _ => SetGuide(_priorEntries.Pop(), false);
|
||||
}
|
||||
|
||||
public void HandleClick(string link)
|
||||
{
|
||||
SetGuide(link);
|
||||
}
|
||||
|
||||
private void SetGuide(ProtoId<GuideEntryPrototype>? entry = null, bool addToPrior = true)
|
||||
{
|
||||
var coreEntry = UserInterfaceManager.GetUIController<InfoUIController>().GetCoreRuleEntry();
|
||||
entry ??= coreEntry;
|
||||
|
||||
Scroll.SetScrollValue(default);
|
||||
RulesContainer.Children.Clear();
|
||||
if (!_parsingMan.TryAddMarkup(RulesContainer, entry.Value))
|
||||
return;
|
||||
|
||||
if (addToPrior && _currentEntry != null)
|
||||
_priorEntries.Push(_currentEntry);
|
||||
_currentEntry = entry.Value;
|
||||
|
||||
HomeButton.Visible = entry.Value != coreEntry.Id;
|
||||
BackButton.Visible = _priorEntries.Count != 0 && _priorEntries.Peek() != entry.Value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,105 +0,0 @@
|
|||
using Content.Client.Lobby;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Info;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Client.Info;
|
||||
|
||||
public sealed class RulesManager : SharedRulesManager
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
|
||||
|
||||
private InfoSection rulesSection = new InfoSection("", "", false);
|
||||
private bool _shouldShowRules = false;
|
||||
|
||||
private RulesPopup? _activePopup;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<ShouldShowRulesPopupMessage>(OnShouldShowRules);
|
||||
_netManager.RegisterNetMessage<ShowRulesPopupMessage>(OnShowRulesPopupMessage);
|
||||
_netManager.RegisterNetMessage<RulesAcceptedMessage>();
|
||||
_stateManager.OnStateChanged += OnStateChanged;
|
||||
|
||||
_consoleHost.RegisterCommand("fuckrules", "", "", (_, _, _) =>
|
||||
{
|
||||
OnAcceptPressed();
|
||||
});
|
||||
}
|
||||
|
||||
private void OnShouldShowRules(ShouldShowRulesPopupMessage message)
|
||||
{
|
||||
_shouldShowRules = true;
|
||||
}
|
||||
|
||||
private void OnShowRulesPopupMessage(ShowRulesPopupMessage message)
|
||||
{
|
||||
ShowRules(message.PopupTime);
|
||||
}
|
||||
|
||||
private void OnStateChanged(StateChangedEventArgs args)
|
||||
{
|
||||
if (args.NewState is not (GameplayState or LobbyState))
|
||||
return;
|
||||
|
||||
if (!_shouldShowRules)
|
||||
return;
|
||||
|
||||
_shouldShowRules = false;
|
||||
|
||||
ShowRules(_configManager.GetCVar(CCVars.RulesWaitTime));
|
||||
}
|
||||
|
||||
private void ShowRules(float time)
|
||||
{
|
||||
if (_activePopup != null)
|
||||
return;
|
||||
|
||||
_activePopup = new RulesPopup
|
||||
{
|
||||
Timer = time
|
||||
};
|
||||
|
||||
_activePopup.OnQuitPressed += OnQuitPressed;
|
||||
_activePopup.OnAcceptPressed += OnAcceptPressed;
|
||||
_userInterfaceManager.WindowRoot.AddChild(_activePopup);
|
||||
LayoutContainer.SetAnchorPreset(_activePopup, LayoutContainer.LayoutPreset.Wide);
|
||||
}
|
||||
|
||||
private void OnQuitPressed()
|
||||
{
|
||||
_consoleHost.ExecuteCommand("quit");
|
||||
}
|
||||
|
||||
private void OnAcceptPressed()
|
||||
{
|
||||
_netManager.ClientSendMessage(new RulesAcceptedMessage());
|
||||
|
||||
_activePopup?.Orphan();
|
||||
_activePopup = null;
|
||||
}
|
||||
|
||||
public void UpdateRules()
|
||||
{
|
||||
var rules = _sysMan.GetEntitySystem<InfoSystem>().Rules;
|
||||
rulesSection.SetText(rules.Title, rules.Text, true);
|
||||
}
|
||||
|
||||
public Control RulesSection()
|
||||
{
|
||||
rulesSection = new InfoSection("", "", false);
|
||||
UpdateRules();
|
||||
return rulesSection;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,20 +5,20 @@
|
|||
MouseFilter="Stop">
|
||||
<parallax:ParallaxControl />
|
||||
<Control VerticalExpand="True"
|
||||
MaxWidth="650">
|
||||
MaxWidth="800"
|
||||
MaxHeight="900">
|
||||
<PanelContainer StyleClasses="windowPanel" />
|
||||
<ScrollContainer HScrollEnabled="False">
|
||||
<BoxContainer Orientation="Vertical" SeparationOverride="10">
|
||||
<info:RulesControl />
|
||||
<BoxContainer Orientation="Vertical" SeparationOverride="10" Margin="10 10 5 10">
|
||||
<info:RulesControl/>
|
||||
<Label Name="WaitLabel" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="AcceptButton"
|
||||
Text="{Loc 'ui-rules-accept'}"
|
||||
Disabled="True" />
|
||||
<Button Name="QuitButton"
|
||||
StyleClasses="Caution"
|
||||
Text="{Loc 'ui-escape-quit'}" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</Control>
|
||||
</Control>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
using System;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Info;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using Content.Client.Clickable;
|
|||
using Content.Client.Corvax.TTS;
|
||||
using Content.Client.Eui;
|
||||
using Content.Client.GhostKick;
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Launcher;
|
||||
using Content.Client.Parallax.Managers;
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
|
|
@ -42,7 +41,6 @@ namespace Content.Client.IoC
|
|||
collection.Register<EuiManager, EuiManager>();
|
||||
collection.Register<IVoteManager, VoteManager>();
|
||||
collection.Register<ChangelogManager, ChangelogManager>();
|
||||
collection.Register<RulesManager, RulesManager>();
|
||||
collection.Register<ViewportManager, ViewportManager>();
|
||||
collection.Register<ISharedAdminLogManager, SharedAdminLogManager>();
|
||||
collection.Register<GhostKickManager>();
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ namespace Content.Client.LateJoin
|
|||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
|
||||
var jobIcon = _prototypeManager.Index<StatusIconPrototype>(prototype.Icon);
|
||||
var jobIcon = _prototypeManager.Index(prototype.Icon);
|
||||
icon.Texture = _sprites.Frame0(jobIcon.Icon);
|
||||
jobSelector.AddChild(icon);
|
||||
|
||||
|
|
@ -290,7 +290,7 @@ namespace Content.Client.LateJoin
|
|||
}
|
||||
}
|
||||
|
||||
private void JobsAvailableUpdated(IReadOnlyDictionary<NetEntity, Dictionary<string, uint?>> updatedJobs)
|
||||
private void JobsAvailableUpdated(IReadOnlyDictionary<NetEntity, Dictionary<ProtoId<JobPrototype>, int?>> updatedJobs)
|
||||
{
|
||||
foreach (var stationEntries in updatedJobs)
|
||||
{
|
||||
|
|
@ -337,10 +337,10 @@ namespace Content.Client.LateJoin
|
|||
public Label JobLabel { get; }
|
||||
public string JobId { get; }
|
||||
public string JobLocalisedName { get; }
|
||||
public uint? Amount { get; private set; }
|
||||
public int? Amount { get; private set; }
|
||||
private bool _initialised = false;
|
||||
|
||||
public JobButton(Label jobLabel, string jobId, string jobLocalisedName, uint? amount)
|
||||
public JobButton(Label jobLabel, ProtoId<JobPrototype> jobId, string jobLocalisedName, int? amount)
|
||||
{
|
||||
JobLabel = jobLabel;
|
||||
JobId = jobId;
|
||||
|
|
@ -350,7 +350,7 @@ namespace Content.Client.LateJoin
|
|||
_initialised = true;
|
||||
}
|
||||
|
||||
public void RefreshLabel(uint? amount)
|
||||
public void RefreshLabel(int? amount)
|
||||
{
|
||||
if (Amount == amount && _initialised)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -207,8 +207,11 @@ namespace Content.Client.Light
|
|||
|
||||
public static Color GetCurrentRgbColor(TimeSpan curTime, TimeSpan offset, Entity<RgbLightControllerComponent> rgb)
|
||||
{
|
||||
var delta = (float)(curTime - offset).TotalSeconds;
|
||||
var entOffset = Math.Abs(rgb.Owner.Id * 0.09817f);
|
||||
var hue = (delta * rgb.Comp.CycleRate + entOffset) % 1;
|
||||
return Color.FromHsv(new Vector4(
|
||||
(float) (((curTime.TotalSeconds - offset.TotalSeconds) * rgb.Comp.CycleRate + Math.Abs(rgb.Owner.Id * 0.1)) % 1),
|
||||
MathF.Abs(hue),
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Corvax.TTS;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Inventory;
|
||||
|
|
@ -42,6 +43,7 @@ public sealed partial class LobbyUIController : UIController, IOnStateEntered<Lo
|
|||
[UISystemDependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
||||
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
||||
[UISystemDependency] private readonly GuidebookSystem _guide = default!;
|
||||
|
||||
private CharacterSetupGui? _characterSetup;
|
||||
private HumanoidProfileEditor? _profileEditor;
|
||||
|
|
@ -233,6 +235,8 @@ public sealed partial class LobbyUIController : UIController, IOnStateEntered<Lo
|
|||
_requirements,
|
||||
_markings);
|
||||
|
||||
_profileEditor.OnOpenGuidebook += _guide.OpenHelp;
|
||||
|
||||
_characterSetup = new CharacterSetupGui(EntityManager, _prototypeManager, _resourceCache, _preferencesManager, _profileEditor);
|
||||
|
||||
_characterSetup.CloseButton.OnPressed += _ =>
|
||||
|
|
@ -303,7 +307,7 @@ public sealed partial class LobbyUIController : UIController, IOnStateEntered<Lo
|
|||
{
|
||||
var highPriorityJob = profile.JobPriorities.FirstOrDefault(p => p.Value == JobPriority.High).Key;
|
||||
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?)
|
||||
return _prototypeManager.Index<JobPrototype>(highPriorityJob ?? SharedGameTicker.FallbackOverflowJob);
|
||||
return _prototypeManager.Index<JobPrototype>(highPriorityJob.Id ?? SharedGameTicker.FallbackOverflowJob);
|
||||
}
|
||||
|
||||
public void GiveDummyLoadout(EntityUid uid, RoleLoadout? roleLoadout)
|
||||
|
|
|
|||
|
|
@ -53,9 +53,9 @@ public sealed partial class CharacterPickerButton : ContainerButton
|
|||
.LoadProfileEntity(humanoid, null, true);
|
||||
|
||||
var highPriorityJob = humanoid.JobPriorities.SingleOrDefault(p => p.Value == JobPriority.High).Key;
|
||||
if (highPriorityJob != null)
|
||||
if (highPriorityJob != default)
|
||||
{
|
||||
var jobName = prototypeManager.Index<JobPrototype>(highPriorityJob).LocalizedName;
|
||||
var jobName = prototypeManager.Index(highPriorityJob).LocalizedName;
|
||||
description = $"{description}\n{jobName}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Lobby.UI.Loadouts;
|
||||
using Content.Client.Lobby.UI.Roles;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Corvax.CCCVars;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Guidebook;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.Traits;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
|
|
@ -96,6 +96,8 @@ namespace Content.Client.Lobby.UI
|
|||
[ValidatePrototypeId<GuideEntryPrototype>]
|
||||
private const string DefaultSpeciesGuidebook = "Species";
|
||||
|
||||
public event Action<List<ProtoId<GuideEntryPrototype>>>? OnOpenGuidebook;
|
||||
|
||||
private ISawmill _sawmill;
|
||||
|
||||
public HumanoidProfileEditor(
|
||||
|
|
@ -383,6 +385,7 @@ namespace Content.Client.Lobby.UI
|
|||
{
|
||||
PreferenceUnavailableButton.SelectId(args.Id);
|
||||
Profile = Profile?.WithPreferenceUnavailable((PreferenceUnavailableMode) args.Id);
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
_jobCategories = new Dictionary<string, BoxContainer>();
|
||||
|
|
@ -479,38 +482,96 @@ namespace Content.Client.Lobby.UI
|
|||
var traits = _prototypeManager.EnumeratePrototypes<TraitPrototype>().OrderBy(t => Loc.GetString(t.Name)).ToList();
|
||||
TabContainer.SetTabTitle(3, Loc.GetString("humanoid-profile-editor-traits-tab"));
|
||||
|
||||
if (traits.Count > 0)
|
||||
{
|
||||
foreach (var trait in traits)
|
||||
{
|
||||
var selector = new TraitPreferenceSelector(trait);
|
||||
|
||||
if (Profile?.TraitPreferences.Contains(trait.ID) == true)
|
||||
{
|
||||
selector.Preference = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
selector.Preference = false;
|
||||
}
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithTraitPreference(trait.ID, preference);
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
TraitsList.AddChild(selector);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (traits.Count < 1)
|
||||
{
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
// TODO: Localise
|
||||
Text = "No traits available :(",
|
||||
Text = Loc.GetString("humanoid-profile-editor-no-traits"),
|
||||
FontColorOverride = Color.Gray,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
//Setup model
|
||||
Dictionary<string, List<string>> model = new();
|
||||
List<string> defaultTraits = new();
|
||||
model.Add("default", defaultTraits);
|
||||
|
||||
foreach (var trait in traits)
|
||||
{
|
||||
if (trait.Category == null)
|
||||
{
|
||||
defaultTraits.Add(trait.ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!model.ContainsKey(trait.Category))
|
||||
{
|
||||
model.Add(trait.Category, new());
|
||||
}
|
||||
model[trait.Category].Add(trait.ID);
|
||||
}
|
||||
|
||||
//Create UI view from model
|
||||
foreach (var (categoryId, traitId) in model)
|
||||
{
|
||||
TraitCategoryPrototype? category = null;
|
||||
if (categoryId != "default")
|
||||
{
|
||||
category = _prototypeManager.Index<TraitCategoryPrototype>(categoryId);
|
||||
// Label
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString(category.Name),
|
||||
Margin = new Thickness(0, 10, 0, 0),
|
||||
StyleClasses = { StyleBase.StyleClassLabelHeading },
|
||||
});
|
||||
}
|
||||
|
||||
List<TraitPreferenceSelector?> selectors = new();
|
||||
var selectionCount = 0;
|
||||
|
||||
foreach (var traitProto in traitId)
|
||||
{
|
||||
var trait = _prototypeManager.Index<TraitPrototype>(traitProto);
|
||||
var selector = new TraitPreferenceSelector(trait);
|
||||
|
||||
selector.Preference = Profile?.TraitPreferences.Contains(trait.ID) == true;
|
||||
if (selector.Preference)
|
||||
selectionCount += trait.Cost;
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithTraitPreference(trait.ID, categoryId, preference);
|
||||
SetDirty();
|
||||
RefreshTraits(); // If too many traits are selected, they will be reset to the real value.
|
||||
};
|
||||
selectors.Add(selector);
|
||||
}
|
||||
|
||||
// Selection counter
|
||||
if (category is { MaxTraitPoints: >= 0 })
|
||||
{
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("humanoid-profile-editor-trait-count-hint", ("current", selectionCount) ,("max", category.MaxTraitPoints)),
|
||||
FontColorOverride = Color.Gray
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var selector in selectors)
|
||||
{
|
||||
if (selector == null)
|
||||
continue;
|
||||
|
||||
if (category is { MaxTraitPoints: >= 0 } &&
|
||||
selector.Cost + selectionCount > category.MaxTraitPoints)
|
||||
{
|
||||
selector.Checkbox.Label.FontColorOverride = Color.Red;
|
||||
}
|
||||
|
||||
TraitsList.AddChild(selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -569,13 +630,15 @@ namespace Content.Client.Lobby.UI
|
|||
{
|
||||
Margin = new Thickness(3f, 3f, 3f, 0f),
|
||||
};
|
||||
selector.OnOpenGuidebook += OnOpenGuidebook;
|
||||
|
||||
var title = Loc.GetString(antag.Name);
|
||||
var description = Loc.GetString(antag.Objective);
|
||||
selector.Setup(items, title, 250, description);
|
||||
selector.Setup(items, title, 250, description, guides: antag.Guides);
|
||||
selector.Select(Profile?.AntagPreferences.Contains(antag.ID) == true ? 0 : 1);
|
||||
|
||||
if (!_requirements.CheckRoleTime(antag.Requirements, out var reason))
|
||||
var requirements = _entManager.System<SharedRoleSystem>().GetAntagRequirement(antag);
|
||||
if (!_requirements.CheckRoleTime(requirements, out var reason))
|
||||
{
|
||||
selector.LockRequirements(reason);
|
||||
Profile = Profile?.WithAntagPreference(antag.ID, false);
|
||||
|
|
@ -708,6 +771,10 @@ namespace Content.Client.Lobby.UI
|
|||
|
||||
private void OnSpeciesInfoButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
// TODO GUIDEBOOK
|
||||
// make the species guide book a field on the species prototype.
|
||||
// I.e., do what jobs/antags do.
|
||||
|
||||
var guidebookController = UserInterfaceManager.GetUIController<GuidebookUIController>();
|
||||
var species = Profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||
var page = DefaultSpeciesGuidebook;
|
||||
|
|
@ -716,10 +783,10 @@ namespace Content.Client.Lobby.UI
|
|||
|
||||
if (_prototypeManager.TryIndex<GuideEntryPrototype>(DefaultSpeciesGuidebook, out var guideRoot))
|
||||
{
|
||||
var dict = new Dictionary<string, GuideEntry>();
|
||||
var dict = new Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry>();
|
||||
dict.Add(DefaultSpeciesGuidebook, guideRoot);
|
||||
//TODO: Don't close the guidebook if its already open, just go to the correct page
|
||||
guidebookController.ToggleGuidebook(dict, includeChildren:true, selected: page);
|
||||
guidebookController.OpenGuidebook(dict, includeChildren:true, selected: page);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -814,15 +881,16 @@ namespace Content.Client.Lobby.UI
|
|||
{
|
||||
Margin = new Thickness(3f, 3f, 3f, 0f),
|
||||
};
|
||||
selector.OnOpenGuidebook += OnOpenGuidebook;
|
||||
|
||||
var icon = new TextureRect
|
||||
{
|
||||
TextureScale = new Vector2(2, 2),
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
var jobIcon = _prototypeManager.Index<StatusIconPrototype>(job.Icon);
|
||||
var jobIcon = _prototypeManager.Index(job.Icon);
|
||||
icon.Texture = jobIcon.Icon.Frame0();
|
||||
selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon);
|
||||
selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon, job.Guides);
|
||||
|
||||
if (!_requirements.IsAllowed(job, out var reason))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Orientation="Horizontal">
|
||||
<Label Name="TitleLabel"
|
||||
Margin="5 0"
|
||||
MouseFilter="Stop"/>
|
||||
<BoxContainer Name="OptionsContainer"
|
||||
SetWidth="400"/>
|
||||
<Label Name="TitleLabel"
|
||||
Margin="5 0"
|
||||
MouseFilter="Stop"/>
|
||||
|
||||
<!--21 was the height of OptionsContainer at the time that this button was added. So I am limiting the texture to 21x21-->
|
||||
<Control SetSize="21 21">
|
||||
<TextureButton Name="Help" StyleClasses="HelpButton"/>
|
||||
</Control>
|
||||
<BoxContainer Name="OptionsContainer"
|
||||
SetWidth="400"/>
|
||||
</BoxContainer>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using System.Numerics;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Guidebook;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Lobby.UI.Roles;
|
||||
|
|
@ -17,8 +19,10 @@ public sealed partial class RequirementsSelector : BoxContainer
|
|||
{
|
||||
private readonly RadioOptions<int> _options;
|
||||
private readonly StripeBack _lockStripe;
|
||||
private List<ProtoId<GuideEntryPrototype>>? _guides;
|
||||
|
||||
public event Action<int>? OnSelected;
|
||||
public event Action<List<ProtoId<GuideEntryPrototype>>>? OnOpenGuidebook;
|
||||
|
||||
public int Selected => _options.SelectedId;
|
||||
|
||||
|
|
@ -60,18 +64,33 @@ public sealed partial class RequirementsSelector : BoxContainer
|
|||
requirementsLabel
|
||||
}
|
||||
};
|
||||
|
||||
Help.OnPressed += _ =>
|
||||
{
|
||||
if (_guides != null)
|
||||
OnOpenGuidebook?.Invoke(_guides);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actually adds the controls.
|
||||
/// </summary>
|
||||
public void Setup((string, int)[] items, string title, int titleSize, string? description, TextureRect? icon = null)
|
||||
public void Setup(
|
||||
(string, int)[] items,
|
||||
string title,
|
||||
int titleSize,
|
||||
string? description,
|
||||
TextureRect? icon = null,
|
||||
List<ProtoId<GuideEntryPrototype>>? guides = null)
|
||||
{
|
||||
foreach (var (text, value) in items)
|
||||
{
|
||||
_options.AddItem(Loc.GetString(text), value);
|
||||
}
|
||||
|
||||
Help.Visible = guides != null;
|
||||
_guides = guides;
|
||||
|
||||
TitleLabel.Text = title;
|
||||
TitleLabel.MinSize = new Vector2(titleSize, 0f);
|
||||
TitleLabel.ToolTip = description;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<BoxContainer Name="Container"
|
||||
Orientation="Horizontal">
|
||||
<CheckBox Name="Checkbox"/>
|
||||
<CheckBox Name="Checkbox" Access="Public"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ namespace Content.Client.Lobby.UI.Roles;
|
|||
[GenerateTypedNameReferences]
|
||||
public sealed partial class TraitPreferenceSelector : Control
|
||||
{
|
||||
public int Cost;
|
||||
|
||||
public bool Preference
|
||||
{
|
||||
get => Checkbox.Pressed;
|
||||
|
|
@ -20,7 +22,12 @@ public sealed partial class TraitPreferenceSelector : Control
|
|||
public TraitPreferenceSelector(TraitPrototype trait)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
Checkbox.Text = Loc.GetString(trait.Name);
|
||||
|
||||
var text = trait.Cost != 0 ? $"[{trait.Cost}] " : "";
|
||||
text += Loc.GetString(trait.Name);
|
||||
|
||||
Cost = trait.Cost;
|
||||
Checkbox.Text = text;
|
||||
Checkbox.OnToggled += OnCheckBoxToggled;
|
||||
|
||||
if (trait.Description is { } desc)
|
||||
|
|
|
|||
20
Content.Client/MapText/MapTextComponent.cs
Normal file
20
Content.Client/MapText/MapTextComponent.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using Content.Shared.MapText;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Content.Client.MapText;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class MapTextComponent : SharedMapTextComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// The font that gets cached on component init or state changes
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public VectorFont? CachedFont;
|
||||
|
||||
/// <summary>
|
||||
/// The text currently being displayed. This is either <see cref="SharedMapTextComponent.Text"/> or the
|
||||
/// localized text <see cref="SharedMapTextComponent.LocText"/> or
|
||||
/// </summary>
|
||||
public string CachedText = string.Empty;
|
||||
}
|
||||
85
Content.Client/MapText/MapTextOverlay.cs
Normal file
85
Content.Client/MapText/MapTextOverlay.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared.MapText;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.MapText;
|
||||
|
||||
/// <summary>
|
||||
/// Draws map text as an overlay
|
||||
/// </summary>
|
||||
public sealed class MapTextOverlay : Overlay
|
||||
{
|
||||
private readonly IConfigurationManager _configManager;
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IUserInterfaceManager _uiManager;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
||||
public MapTextOverlay(
|
||||
IConfigurationManager configManager,
|
||||
IEntityManager entManager,
|
||||
IUserInterfaceManager uiManager,
|
||||
SharedTransformSystem transform,
|
||||
IResourceCache resourceCache,
|
||||
IPrototypeManager prototypeManager)
|
||||
{
|
||||
_configManager = configManager;
|
||||
_entManager = entManager;
|
||||
_uiManager = uiManager;
|
||||
_transform = transform;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (args.ViewportControl == null)
|
||||
return;
|
||||
|
||||
args.DrawingHandle.SetTransform(Matrix3x2.Identity);
|
||||
|
||||
var scale = _configManager.GetCVar(CVars.DisplayUIScale);
|
||||
|
||||
if (scale == 0f)
|
||||
scale = _uiManager.DefaultUIScale;
|
||||
|
||||
DrawWorld(args.ScreenHandle, args, scale);
|
||||
|
||||
args.DrawingHandle.UseShader(null);
|
||||
}
|
||||
|
||||
private void DrawWorld(DrawingHandleScreen handle, OverlayDrawArgs args, float scale)
|
||||
{
|
||||
if ( args.ViewportControl == null)
|
||||
return;
|
||||
|
||||
var matrix = args.ViewportControl.GetWorldToScreenMatrix();
|
||||
var query = _entManager.AllEntityQueryEnumerator<MapTextComponent>();
|
||||
|
||||
// Enlarge bounds to try prevent pop-in due to large text.
|
||||
var bounds = args.WorldBounds.Enlarged(2);
|
||||
|
||||
while(query.MoveNext(out var uid, out var mapText))
|
||||
{
|
||||
var mapPos = _transform.GetMapCoordinates(uid);
|
||||
|
||||
if (mapPos.MapId != args.MapId)
|
||||
continue;
|
||||
|
||||
if (!bounds.Contains(mapPos.Position))
|
||||
continue;
|
||||
|
||||
if (mapText.CachedFont == null)
|
||||
continue;
|
||||
|
||||
var pos = Vector2.Transform(mapPos.Position, matrix) + mapText.Offset;
|
||||
var dimensions = handle.GetDimensions(mapText.CachedFont, mapText.CachedText, scale);
|
||||
handle.DrawString(mapText.CachedFont, pos - dimensions / 2f, mapText.CachedText, scale, mapText.Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
81
Content.Client/MapText/MapTextSystem.cs
Normal file
81
Content.Client/MapText/MapTextSystem.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
using Content.Shared.MapText;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.MapText;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class MapTextSystem : SharedMapTextSystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
|
||||
private MapTextOverlay _overlay = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<MapTextComponent, ComponentStartup>(OnComponentStartup);
|
||||
SubscribeLocalEvent<MapTextComponent, ComponentHandleState>(HandleCompState);
|
||||
|
||||
_overlay = new MapTextOverlay(_configManager, EntityManager, _uiManager, _transform, _resourceCache, _prototypeManager);
|
||||
_overlayManager.AddOverlay(_overlay);
|
||||
|
||||
// TODO move font prototype to robust.shared, then use ProtoId<FontPrototype>
|
||||
DebugTools.Assert(_prototypeManager.HasIndex<FontPrototype>(SharedMapTextComponent.DefaultFont));
|
||||
}
|
||||
|
||||
private void OnComponentStartup(Entity<MapTextComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
CacheText(ent.Comp);
|
||||
// TODO move font prototype to robust.shared, then use ProtoId<FontPrototype>
|
||||
DebugTools.Assert(_prototypeManager.HasIndex<FontPrototype>(ent.Comp.FontId));
|
||||
}
|
||||
|
||||
private void HandleCompState(Entity<MapTextComponent> ent, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not MapTextComponentState state)
|
||||
return;
|
||||
|
||||
ent.Comp.Text = state.Text;
|
||||
ent.Comp.LocText = state.LocText;
|
||||
ent.Comp.Color = state.Color;
|
||||
ent.Comp.FontId = state.FontId;
|
||||
ent.Comp.FontSize = state.FontSize;
|
||||
ent.Comp.Offset = state.Offset;
|
||||
|
||||
CacheText(ent.Comp);
|
||||
}
|
||||
|
||||
private void CacheText(MapTextComponent component)
|
||||
{
|
||||
component.CachedFont = null;
|
||||
|
||||
component.CachedText = string.IsNullOrWhiteSpace(component.Text)
|
||||
? Loc.GetString(component.LocText)
|
||||
: component.Text;
|
||||
|
||||
if (!_prototypeManager.TryIndex<FontPrototype>(component.FontId, out var fontPrototype))
|
||||
{
|
||||
component.CachedText = Loc.GetString("map-text-font-error");
|
||||
component.Color = Color.Red;
|
||||
|
||||
if(_prototypeManager.TryIndex<FontPrototype>(SharedMapTextComponent.DefaultFont, out var @default))
|
||||
component.CachedFont = new VectorFont(_resourceCache.GetResource<FontResource>(@default.Path), 14);
|
||||
return;
|
||||
}
|
||||
|
||||
var fontResource = _resourceCache.GetResource<FontResource>(fontPrototype.Path);
|
||||
component.CachedFont = new VectorFont(fontResource, component.FontSize);
|
||||
}
|
||||
}
|
||||
|
|
@ -87,6 +87,6 @@ public sealed partial class MaterialStorageControl : ScrollContainer
|
|||
}
|
||||
|
||||
_currentMaterials = mats;
|
||||
NoMatsLabel.Visible = ChildCount == 1;
|
||||
NoMatsLabel.Visible = MaterialList.ChildCount == 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,94 +2,115 @@
|
|||
using Content.Client.Buckle;
|
||||
using Content.Client.Gravity;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Movement.Systems;
|
||||
|
||||
public sealed class WaddleAnimationSystem : EntitySystem
|
||||
public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
|
||||
[Dependency] private readonly GravitySystem _gravity = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
[Dependency] private readonly BuckleSystem _buckle = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, MoveInputEvent>(OnMovementInput);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StartedWaddlingEvent>(OnStartedWalking);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StoppedWaddlingEvent>(OnStoppedWalking);
|
||||
base.Initialize();
|
||||
|
||||
SubscribeAllEvent<StartedWaddlingEvent>(OnStartWaddling);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StunnedEvent>(OnStunned);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, KnockedDownEvent>(OnKnockedDown);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, BuckleChangeEvent>(OnBuckleChange);
|
||||
SubscribeAllEvent<StoppedWaddlingEvent>(OnStopWaddling);
|
||||
}
|
||||
|
||||
private void OnMovementInput(EntityUid entity, WaddleAnimationComponent component, MoveInputEvent args)
|
||||
private void OnStartWaddling(StartedWaddlingEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
// Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume
|
||||
// they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR.
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.HasDirectionalMovement && component.IsCurrentlyWaddling)
|
||||
{
|
||||
var stopped = new StoppedWaddlingEvent(entity);
|
||||
|
||||
RaiseLocalEvent(entity, ref stopped);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Only start waddling if we're not currently AND we're actually moving.
|
||||
if (component.IsCurrentlyWaddling || !args.HasDirectionalMovement)
|
||||
return;
|
||||
|
||||
var started = new StartedWaddlingEvent(entity);
|
||||
|
||||
RaiseLocalEvent(entity, ref started);
|
||||
if (TryComp<WaddleAnimationComponent>(GetEntity(msg.Entity), out var comp))
|
||||
StartWaddling((GetEntity(msg.Entity), comp));
|
||||
}
|
||||
|
||||
private void OnStartedWalking(EntityUid uid, WaddleAnimationComponent component, StartedWaddlingEvent args)
|
||||
private void OnStopWaddling(StoppedWaddlingEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (_animation.HasRunningAnimation(uid, component.KeyName))
|
||||
if (TryComp<WaddleAnimationComponent>(GetEntity(msg.Entity), out var comp))
|
||||
StopWaddling((GetEntity(msg.Entity), comp));
|
||||
}
|
||||
|
||||
private void StartWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
if (_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName))
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(uid, out var mover))
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
return;
|
||||
|
||||
if (_gravity.IsWeightless(uid))
|
||||
if (_gravity.IsWeightless(entity.Owner))
|
||||
return;
|
||||
|
||||
|
||||
if (!_actionBlocker.CanMove(uid, mover))
|
||||
if (!_actionBlocker.CanMove(entity.Owner, mover))
|
||||
return;
|
||||
|
||||
// Do nothing if buckled in
|
||||
if (_buckle.IsBuckled(uid))
|
||||
if (_buckle.IsBuckled(entity.Owner))
|
||||
return;
|
||||
|
||||
// Do nothing if crit or dead (for obvious reasons)
|
||||
if (_mobState.IsIncapacitated(uid))
|
||||
if (_mobState.IsIncapacitated(entity.Owner))
|
||||
return;
|
||||
|
||||
var tumbleIntensity = component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity;
|
||||
var len = mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength;
|
||||
PlayWaddleAnimationUsing(
|
||||
(entity.Owner, entity.Comp),
|
||||
CalculateAnimationLength(entity.Comp, mover),
|
||||
CalculateTumbleIntensity(entity.Comp)
|
||||
);
|
||||
}
|
||||
|
||||
component.LastStep = !component.LastStep;
|
||||
component.IsCurrentlyWaddling = true;
|
||||
private static float CalculateTumbleIntensity(WaddleAnimationComponent component)
|
||||
{
|
||||
return component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity;
|
||||
}
|
||||
|
||||
private static float CalculateAnimationLength(WaddleAnimationComponent component, InputMoverComponent mover)
|
||||
{
|
||||
return mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength;
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(Entity<WaddleAnimationComponent> entity, ref AnimationCompletedEvent args)
|
||||
{
|
||||
if (args.Key != entity.Comp.KeyName)
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
return;
|
||||
|
||||
PlayWaddleAnimationUsing(
|
||||
(entity.Owner, entity.Comp),
|
||||
CalculateAnimationLength(entity.Comp, mover),
|
||||
CalculateTumbleIntensity(entity.Comp)
|
||||
);
|
||||
}
|
||||
|
||||
private void StopWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
if (!_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName))
|
||||
return;
|
||||
|
||||
_animation.Stop(entity.Owner, entity.Comp.KeyName);
|
||||
|
||||
if (!TryComp<SpriteComponent>(entity.Owner, out var sprite))
|
||||
return;
|
||||
|
||||
sprite.Offset = new Vector2();
|
||||
sprite.Rotation = Angle.FromDegrees(0);
|
||||
}
|
||||
|
||||
private void PlayWaddleAnimationUsing(Entity<WaddleAnimationComponent> entity, float len, float tumbleIntensity)
|
||||
{
|
||||
entity.Comp.LastStep = !entity.Comp.LastStep;
|
||||
|
||||
var anim = new Animation()
|
||||
{
|
||||
|
|
@ -116,58 +137,13 @@ public sealed class WaddleAnimationSystem : EntitySystem
|
|||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), 0),
|
||||
new AnimationTrackProperty.KeyFrame(component.HopIntensity, len/2),
|
||||
new AnimationTrackProperty.KeyFrame(entity.Comp.HopIntensity, len/2),
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), len/2),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_animation.Play(uid, anim, component.KeyName);
|
||||
}
|
||||
|
||||
private void OnStoppedWalking(EntityUid uid, WaddleAnimationComponent component, StoppedWaddlingEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(EntityUid uid, WaddleAnimationComponent component, AnimationCompletedEvent args)
|
||||
{
|
||||
var started = new StartedWaddlingEvent(uid);
|
||||
|
||||
RaiseLocalEvent(uid, ref started);
|
||||
}
|
||||
|
||||
private void OnStunned(EntityUid uid, WaddleAnimationComponent component, StunnedEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void OnKnockedDown(EntityUid uid, WaddleAnimationComponent component, KnockedDownEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void OnBuckleChange(EntityUid uid, WaddleAnimationComponent component, BuckleChangeEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void StopWaddling(EntityUid uid, WaddleAnimationComponent component)
|
||||
{
|
||||
if (!component.IsCurrentlyWaddling)
|
||||
return;
|
||||
|
||||
_animation.Stop(uid, component.KeyName);
|
||||
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sprite.Offset = new Vector2();
|
||||
sprite.Rotation = Angle.FromDegrees(0);
|
||||
|
||||
component.IsCurrentlyWaddling = false;
|
||||
_animation.Play(entity.Owner, anim, entity.Comp.KeyName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public sealed class TargetOutlineSystem : EntitySystem
|
|||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
|
||||
private bool _enabled = false;
|
||||
|
||||
|
|
@ -137,7 +138,7 @@ public sealed class TargetOutlineSystem : EntitySystem
|
|||
|
||||
// check the entity whitelist
|
||||
if (valid && Whitelist != null)
|
||||
valid = Whitelist.IsValid(entity);
|
||||
valid = _whitelistSystem.IsWhitelistPass(Whitelist, entity);
|
||||
|
||||
// and check the cancellable event
|
||||
if (valid && ValidationEvent != null)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
using System.Numerics;
|
||||
using Content.Client.StatusIcon;
|
||||
using Content.Client.UserInterface.Systems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Shared.Maths.Color;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
|
@ -19,19 +22,27 @@ namespace Content.Client.Overlays;
|
|||
public sealed class EntityHealthBarOverlay : Overlay
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IPrototypeManager _prototype;
|
||||
|
||||
private readonly SharedTransformSystem _transform;
|
||||
private readonly MobStateSystem _mobStateSystem;
|
||||
private readonly MobThresholdSystem _mobThresholdSystem;
|
||||
private readonly StatusIconSystem _statusIconSystem;
|
||||
private readonly ProgressColorSystem _progressColor;
|
||||
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
public HashSet<string> DamageContainers = new();
|
||||
public ProtoId<StatusIconPrototype>? StatusIcon;
|
||||
|
||||
public EntityHealthBarOverlay(IEntityManager entManager)
|
||||
public EntityHealthBarOverlay(IEntityManager entManager, IPrototypeManager prototype)
|
||||
{
|
||||
_entManager = entManager;
|
||||
_prototype = prototype;
|
||||
_transform = _entManager.System<SharedTransformSystem>();
|
||||
_mobStateSystem = _entManager.System<MobStateSystem>();
|
||||
_mobThresholdSystem = _entManager.System<MobThresholdSystem>();
|
||||
_statusIconSystem = _entManager.System<StatusIconSystem>();
|
||||
_progressColor = _entManager.System<ProgressColorSystem>();
|
||||
}
|
||||
|
||||
|
|
@ -44,6 +55,7 @@ public sealed class EntityHealthBarOverlay : Overlay
|
|||
const float scale = 1f;
|
||||
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation);
|
||||
_prototype.TryIndex(StatusIcon, out var statusIcon);
|
||||
|
||||
var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>();
|
||||
while (query.MoveNext(out var uid,
|
||||
|
|
@ -52,31 +64,23 @@ public sealed class EntityHealthBarOverlay : Overlay
|
|||
out var damageableComponent,
|
||||
out var spriteComponent))
|
||||
{
|
||||
if (_entManager.TryGetComponent<MetaDataComponent>(uid, out var metaDataComponent) &&
|
||||
metaDataComponent.Flags.HasFlag(MetaDataFlags.InContainer))
|
||||
{
|
||||
if (statusIcon != null && !_statusIconSystem.IsVisible((uid, _entManager.GetComponent<MetaDataComponent>(uid)), statusIcon))
|
||||
continue;
|
||||
}
|
||||
|
||||
// We want the stealth user to still be able to see his health bar himself
|
||||
if (!xformQuery.TryGetComponent(uid, out var xform) ||
|
||||
xform.MapID != args.MapId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (damageableComponent.DamageContainerID == null || !DamageContainers.Contains(damageableComponent.DamageContainerID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// we use the status icon component bounds if specified otherwise use sprite
|
||||
var bounds = _entManager.GetComponentOrNull<StatusIconComponent>(uid)?.Bounds ?? spriteComponent.Bounds;
|
||||
var worldPos = _transform.GetWorldPosition(xform, xformQuery);
|
||||
|
||||
if (!bounds.Translated(worldPos).Intersects(args.WorldAABB))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// we are all progressing towards death every day
|
||||
if (CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent) is not { } deathProgress)
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ public sealed class ShowCriminalRecordIconsSystem : EquipmentHudSystem<ShowCrimi
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, CriminalRecordComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.StatusIcon.Id, out var iconPrototype))
|
||||
if (_prototype.TryIndex(component.StatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ using Content.Shared.Inventory.Events;
|
|||
using Content.Shared.Overlays;
|
||||
using Robust.Client.Graphics;
|
||||
using System.Linq;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
|
|
@ -11,6 +13,7 @@ namespace Content.Client.Overlays;
|
|||
public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComponent>
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
private EntityHealthBarOverlay _overlay = default!;
|
||||
|
||||
|
|
@ -18,16 +21,21 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
|
|||
{
|
||||
base.Initialize();
|
||||
|
||||
_overlay = new(EntityManager);
|
||||
_overlay = new(EntityManager, _prototype);
|
||||
}
|
||||
|
||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component)
|
||||
{
|
||||
base.UpdateInternal(component);
|
||||
|
||||
foreach (var damageContainerId in component.Components.SelectMany(x => x.DamageContainers))
|
||||
foreach (var comp in component.Components)
|
||||
{
|
||||
_overlay.DamageContainers.Add(damageContainerId);
|
||||
foreach (var damageContainerId in comp.DamageContainers)
|
||||
{
|
||||
_overlay.DamageContainers.Add(damageContainerId);
|
||||
}
|
||||
|
||||
_overlay.StatusIcon = comp.HealthStatusIcon;
|
||||
}
|
||||
|
||||
if (!_overlayMan.HasOverlay<EntityHealthBarOverlay>())
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
|||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DamageableComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
|
||||
}
|
||||
|
||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthIconsComponent> component)
|
||||
|
|
@ -46,7 +45,7 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
|||
|
||||
private void OnGetStatusIconsEvent(Entity<DamageableComponent> entity, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (!IsActive || args.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
var healthIcons = DecideHealthIcons(entity);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public sealed class ShowHungerIconsSystem : EquipmentHudSystem<ShowHungerIconsCo
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_hunger.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponen
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
var iconId = JobIconForNoId;
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ public sealed class ShowMindShieldIconsSystem : EquipmentHudSystem<ShowMindShiel
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.MindShieldStatusIcon.Id, out var iconPrototype))
|
||||
if (_prototype.TryIndex(component.MindShieldStatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,10 @@ public sealed class ShowSyndicateIconsSystem : EquipmentHudSystem<ShowSyndicateI
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.SyndStatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsCo
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_thirst.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
|
|
|
|||
|
|
@ -21,8 +21,16 @@ namespace Content.Client.PDA
|
|||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
if (_menu == null)
|
||||
CreateMenu();
|
||||
|
||||
_menu?.OpenCenteredLeft();
|
||||
}
|
||||
|
||||
private void CreateMenu()
|
||||
{
|
||||
_menu = new PdaMenu();
|
||||
_menu.OpenCenteredLeft();
|
||||
_menu.OnClose += Close;
|
||||
_menu.FlashLightToggleButton.OnToggled += _ =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -106,7 +106,13 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager
|
|||
if (player == null)
|
||||
return true;
|
||||
|
||||
return CheckRoleTime(job.Requirements, out reason);
|
||||
return CheckRoleTime(job, out reason);
|
||||
}
|
||||
|
||||
public bool CheckRoleTime(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||
{
|
||||
var reqs = _entManager.System<SharedRoleSystem>().GetJobRequirement(job);
|
||||
return CheckRoleTime(reqs, out reason);
|
||||
}
|
||||
|
||||
public bool CheckRoleTime(HashSet<JobRequirement>? requirements, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||
|
|
|
|||
|
|
@ -1,44 +1,37 @@
|
|||
using Content.Shared.Antag;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Revolutionary;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Revolutionary;
|
||||
|
||||
/// <summary>
|
||||
/// Used for the client to get status icons from other revs.
|
||||
/// </summary>
|
||||
public sealed class RevolutionarySystem : EntitySystem
|
||||
public sealed class RevolutionarySystem : SharedRevolutionarySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RevolutionaryComponent, CanDisplayStatusIconsEvent>(OnCanShowRevIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, CanDisplayStatusIconsEvent>(OnCanShowRevIcon);
|
||||
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetHeadRevIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether a client should display the rev icon.
|
||||
/// </summary>
|
||||
private void OnCanShowRevIcon<T>(EntityUid uid, T comp, ref CanDisplayStatusIconsEvent args) where T : IAntagStatusIconComponent
|
||||
private void GetRevIcon(Entity<RevolutionaryComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
args.Cancelled = !CanDisplayIcon(args.User, comp.IconVisibleToGhost);
|
||||
if (HasComp<HeadRevolutionaryComponent>(ent))
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The criteria that determine whether a client should see Rev/Head rev icons.
|
||||
/// </summary>
|
||||
private bool CanDisplayIcon(EntityUid? uid, bool visibleToGhost)
|
||||
private void GetHeadRevIcon(Entity<HeadRevolutionaryComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(uid) || HasComp<RevolutionaryComponent>(uid))
|
||||
return true;
|
||||
|
||||
if (visibleToGhost && HasComp<GhostComponent>(uid))
|
||||
return true;
|
||||
|
||||
return HasComp<ShowRevIconsComponent>(uid);
|
||||
if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,13 +30,12 @@ public sealed class SSDIndicatorSystem : EntitySystem
|
|||
{
|
||||
if (component.IsSSD &&
|
||||
_cfg.GetCVar(CCVars.ICShowSSDIndicator) &&
|
||||
!args.InContainer &&
|
||||
!_mobState.IsDead(uid) &&
|
||||
!HasComp<ActiveNPCComponent>(uid) &&
|
||||
TryComp<MindContainerComponent>(uid, out var mindContainer) &&
|
||||
mindContainer.ShowExamineInfo)
|
||||
{
|
||||
args.StatusIcons.Add(_prototype.Index<StatusIconPrototype>(component.Icon));
|
||||
args.StatusIcons.Add(_prototype.Index(component.Icon));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Robust.Client.UserInterface;
|
|||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Silicons.Laws.Ui;
|
||||
|
||||
|
|
@ -27,6 +28,8 @@ public sealed partial class LawDisplay : Control
|
|||
var identifier = law.LawIdentifierOverride ?? $"{law.Order}";
|
||||
var lawIdentifier = Loc.GetString("laws-ui-law-header", ("id", identifier));
|
||||
var lawDescription = Loc.GetString(law.LawString);
|
||||
var lawIdentifierPlaintext = FormattedMessage.RemoveMarkupPermissive(lawIdentifier);
|
||||
var lawDescriptionPlaintext = FormattedMessage.RemoveMarkupPermissive(lawDescription);
|
||||
|
||||
LawNumberLabel.SetMarkup(lawIdentifier);
|
||||
LawLabel.SetMessage(lawDescription);
|
||||
|
|
@ -46,7 +49,7 @@ public sealed partial class LawDisplay : Control
|
|||
|
||||
localButton.OnPressed += _ =>
|
||||
{
|
||||
_chatManager.SendMessage($"{lawIdentifier}: {lawDescription}", ChatSelectChannel.Local);
|
||||
_chatManager.SendMessage($"{lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Local);
|
||||
};
|
||||
|
||||
LawAnnouncementButtons.AddChild(localButton);
|
||||
|
|
@ -73,9 +76,9 @@ public sealed partial class LawDisplay : Control
|
|||
switch (radioChannel)
|
||||
{
|
||||
case SharedChatSystem.CommonChannel:
|
||||
_chatManager.SendMessage($"{SharedChatSystem.RadioCommonPrefix} {lawIdentifier}: {lawDescription}", ChatSelectChannel.Radio); break;
|
||||
_chatManager.SendMessage($"{SharedChatSystem.RadioCommonPrefix} {lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Radio); break;
|
||||
default:
|
||||
_chatManager.SendMessage($"{SharedChatSystem.RadioChannelPrefix}{radioChannelProto.KeyCode} {lawIdentifier}: {lawDescription}", ChatSelectChannel.Radio); break;
|
||||
_chatManager.SendMessage($"{SharedChatSystem.RadioChannelPrefix}{radioChannelProto.KeyCode} {lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Radio); break;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public sealed class StatusIconOverlay : Overlay
|
|||
var query = _entity.AllEntityQueryEnumerator<StatusIconComponent, SpriteComponent, TransformComponent, MetaDataComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp, out var sprite, out var xform, out var meta))
|
||||
{
|
||||
if (xform.MapID != args.MapId)
|
||||
if (xform.MapID != args.MapId || !sprite.Visible)
|
||||
continue;
|
||||
|
||||
var bounds = comp.Bounds ?? sprite.Bounds;
|
||||
|
|
@ -72,6 +72,8 @@ public sealed class StatusIconOverlay : Overlay
|
|||
|
||||
foreach (var proto in icons)
|
||||
{
|
||||
if (!_statusIcon.IsVisible((uid, meta), proto))
|
||||
continue;
|
||||
|
||||
var curTime = _timing.RealTime;
|
||||
var texture = _sprite.GetFrame(proto.Icon, curTime);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.StatusIcon;
|
||||
|
|
@ -13,6 +17,8 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
|
|||
{
|
||||
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
|
||||
|
||||
private bool _globalEnabled;
|
||||
private bool _localEnabled;
|
||||
|
|
@ -54,10 +60,34 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
|
|||
if (meta.EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
return list;
|
||||
|
||||
var inContainer = (meta.Flags & MetaDataFlags.InContainer) != 0;
|
||||
var ev = new GetStatusIconsEvent(list, inContainer);
|
||||
var ev = new GetStatusIconsEvent(list);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
return ev.StatusIcons;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For overlay to check if an entity can be seen.
|
||||
/// </summary>
|
||||
public bool IsVisible(Entity<MetaDataComponent> ent, StatusIconData data)
|
||||
{
|
||||
var viewer = _playerManager.LocalSession?.AttachedEntity;
|
||||
|
||||
// Always show our icons to our entity
|
||||
if (viewer == ent.Owner)
|
||||
return true;
|
||||
|
||||
if (data.VisibleToGhosts && HasComp<GhostComponent>(viewer))
|
||||
return true;
|
||||
|
||||
if (data.HideInContainer && (ent.Comp.Flags & MetaDataFlags.InContainer) != 0)
|
||||
return false;
|
||||
|
||||
if (data.HideOnStealth && TryComp<StealthComponent>(ent, out var stealth) && stealth.Enabled)
|
||||
return false;
|
||||
|
||||
if (data.ShowTo != null && !_entityWhitelist.IsValid(data.ShowTo, viewer))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Client.Interactable.Components;
|
||||
using Content.Client.StatusIcon;
|
||||
using Content.Shared.Stealth;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
|
@ -18,6 +19,7 @@ public sealed class StealthSystem : SharedStealthSystem
|
|||
base.Initialize();
|
||||
|
||||
_shader = _protoMan.Index<ShaderPrototype>("Stealth").InstanceUnique();
|
||||
|
||||
SubscribeLocalEvent<StealthComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<StealthComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<StealthComponent, BeforePostShaderRenderEvent>(OnShaderRender);
|
||||
|
|
@ -93,4 +95,3 @@ public sealed class StealthSystem : SharedStealthSystem
|
|||
args.Sprite.Color = new Color(visibility, visibility, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ namespace Content.Client.Stylesheets
|
|||
public const string StyleClassLabelSmall = "LabelSmall";
|
||||
public const string StyleClassButtonBig = "ButtonBig";
|
||||
|
||||
public const string StyleClassButtonHelp = "HelpButton";
|
||||
|
||||
public const string StyleClassPopupMessageSmall = "PopupMessageSmall";
|
||||
public const string StyleClassPopupMessageSmallCaution = "PopupMessageSmallCaution";
|
||||
public const string StyleClassPopupMessageMedium = "PopupMessageMedium";
|
||||
|
|
@ -1346,6 +1348,10 @@ namespace Content.Client.Stylesheets
|
|||
new StyleProperty(PanelContainer.StylePropertyPanel, new StyleBoxFlat { BackgroundColor = NanoGold, ContentMarginBottomOverride = 2, ContentMarginLeftOverride = 2}),
|
||||
}),
|
||||
|
||||
Element<TextureButton>()
|
||||
.Class(StyleClassButtonHelp)
|
||||
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/Interface/VerbIcons/information.svg.192dpi.png")),
|
||||
|
||||
// Labels ---
|
||||
Element<Label>().Class(StyleClassLabelBig)
|
||||
.Prop(Label.StylePropertyFont, notoSans16),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Numerics;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Guidebook.Components;
|
||||
using Content.Shared.Guidebook;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
|
@ -32,8 +33,8 @@ namespace Content.Client.UserInterface.Controls
|
|||
set => WindowTitle.Text = value;
|
||||
}
|
||||
|
||||
private List<string>? _helpGuidebookIds;
|
||||
public List<string>? HelpGuidebookIds
|
||||
private List<ProtoId<GuideEntryPrototype>>? _helpGuidebookIds;
|
||||
public List<ProtoId<GuideEntryPrototype>>? HelpGuidebookIds
|
||||
{
|
||||
get => _helpGuidebookIds;
|
||||
set
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ public sealed class ScrambleTag : IMarkupTag
|
|||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private const int MaxScrambleLength = 32;
|
||||
|
||||
public string Name => "scramble";
|
||||
|
||||
public string TextBefore(MarkupNode node)
|
||||
|
|
@ -29,8 +31,9 @@ public sealed class ScrambleTag : IMarkupTag
|
|||
var seed = (int) (_timing.CurTime.TotalMilliseconds / rate);
|
||||
var rand = new Random(seed + node.GetHashCode());
|
||||
var charOptions = chars.ToCharArray();
|
||||
var realLength = MathF.Min(length.Value, MaxScrambleLength);
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < length; i++)
|
||||
for (var i = 0; i < realLength; i++)
|
||||
{
|
||||
var index = rand.Next() % charOptions.Length;
|
||||
sb.Append(charOptions[index]);
|
||||
|
|
|
|||
|
|
@ -289,6 +289,10 @@ public sealed class ActionButton : Control, IEntityControl
|
|||
{
|
||||
if (_action.IconOn != null)
|
||||
SetActionIcon(_spriteSys.Frame0(_action.IconOn));
|
||||
else if (_action.Icon != null)
|
||||
SetActionIcon(_spriteSys.Frame0(_action.Icon));
|
||||
else
|
||||
SetActionIcon(null);
|
||||
|
||||
if (_action.BackgroundOn != null)
|
||||
_buttonBackgroundTexture = _spriteSys.Frame0(_action.BackgroundOn);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Content.Client.Administration.Systems;
|
|||
using Content.Client.Administration.UI;
|
||||
using Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||
using Content.Client.Administration.UI.Tabs.PanicBunkerTab;
|
||||
using Content.Client.Administration.UI.Tabs.BabyJailTab;
|
||||
using Content.Client.Administration.UI.Tabs.PlayerTab;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Lobby;
|
||||
|
|
@ -37,11 +38,13 @@ public sealed class AdminUIController : UIController,
|
|||
private AdminMenuWindow? _window;
|
||||
private MenuButton? AdminButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.AdminButton;
|
||||
private PanicBunkerStatus? _panicBunker;
|
||||
private BabyJailStatus? _babyJail;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeNetworkEvent<PanicBunkerChangedEvent>(OnPanicBunkerUpdated);
|
||||
SubscribeNetworkEvent<BabyJailChangedEvent>(OnBabyJailUpdated);
|
||||
}
|
||||
|
||||
private void OnPanicBunkerUpdated(PanicBunkerChangedEvent msg, EntitySessionEventArgs args)
|
||||
|
|
@ -56,6 +59,18 @@ public sealed class AdminUIController : UIController,
|
|||
}
|
||||
}
|
||||
|
||||
private void OnBabyJailUpdated(BabyJailChangedEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var showDialog = _babyJail == null && msg.Status.Enabled;
|
||||
_babyJail = msg.Status;
|
||||
_window?.BabyJailControl.UpdateStatus(msg.Status);
|
||||
|
||||
if (showDialog)
|
||||
{
|
||||
UIManager.CreateWindow<BabyJailStatusWindow>().OpenCentered();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnStateEntered(GameplayState state)
|
||||
{
|
||||
EnsureWindow();
|
||||
|
|
@ -101,6 +116,13 @@ public sealed class AdminUIController : UIController,
|
|||
if (_panicBunker != null)
|
||||
_window.PanicBunkerControl.UpdateStatus(_panicBunker);
|
||||
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
if (_babyJail != null)
|
||||
_window.BabyJailControl.UpdateStatus(_babyJail);
|
||||
|
||||
_window.PlayerTabControl.OnEntryKeyBindDown += PlayerTabEntryKeyBindDown;
|
||||
_window.ObjectsTabControl.OnEntryKeyBindDown += ObjectsTabEntryKeyBindDown;
|
||||
_window.OnOpen += OnWindowOpen;
|
||||
|
|
@ -198,9 +220,12 @@ public sealed class AdminUIController : UIController,
|
|||
args.Handle();
|
||||
}
|
||||
|
||||
private void ObjectsTabEntryKeyBindDown(ObjectsTabEntry entry, GUIBoundKeyEventArgs args)
|
||||
private void ObjectsTabEntryKeyBindDown(GUIBoundKeyEventArgs args, ListData? data)
|
||||
{
|
||||
var uid = entry.AssocEntity;
|
||||
if (data is not ObjectsListData { Info: var info })
|
||||
return;
|
||||
|
||||
var uid = info.Entity;
|
||||
var function = args.Function;
|
||||
|
||||
if (function == EngineKeyFunctions.UIClick)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Content.Shared.Atmos.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
||||
{
|
||||
|
|
@ -30,7 +29,7 @@ namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
|||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window = new GasTankWindow(this);
|
||||
_window = new GasTankWindow(this, EntMan.GetComponent<MetaDataComponent>(Owner).EntityName);
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,201 +10,194 @@ using Robust.Client.UserInterface.Controls;
|
|||
using Robust.Client.UserInterface.CustomControls;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank;
|
||||
|
||||
public sealed class GasTankWindow
|
||||
: BaseWindow
|
||||
{
|
||||
public sealed class GasTankWindow
|
||||
: BaseWindow
|
||||
private readonly RichTextLabel _lblPressure;
|
||||
private readonly FloatSpinBox _spbPressure;
|
||||
private readonly RichTextLabel _lblInternals;
|
||||
private readonly Button _btnInternals;
|
||||
|
||||
public GasTankWindow(GasTankBoundUserInterface owner, string uidName)
|
||||
{
|
||||
private GasTankBoundUserInterface _owner;
|
||||
private readonly Label _lblName;
|
||||
private readonly BoxContainer _topContainer;
|
||||
private readonly Control _contentContainer;
|
||||
Control contentContainer;
|
||||
BoxContainer topContainer;
|
||||
TextureButton btnClose;
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
var rootContainer = new LayoutContainer { Name = "GasTankRoot" };
|
||||
AddChild(rootContainer);
|
||||
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
|
||||
private readonly IResourceCache _resourceCache = default!;
|
||||
private readonly RichTextLabel _lblPressure;
|
||||
private readonly FloatSpinBox _spbPressure;
|
||||
private readonly RichTextLabel _lblInternals;
|
||||
private readonly Button _btnInternals;
|
||||
|
||||
public GasTankWindow(GasTankBoundUserInterface owner)
|
||||
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
{
|
||||
TextureButton btnClose;
|
||||
_resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
_owner = owner;
|
||||
var rootContainer = new LayoutContainer {Name = "GasTankRoot"};
|
||||
AddChild(rootContainer);
|
||||
Texture = panelTex,
|
||||
Modulate = Color.FromHex("#25252A"),
|
||||
};
|
||||
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
|
||||
var panelTex = _resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
var topPanel = new PanelContainer
|
||||
{
|
||||
PanelOverride = back,
|
||||
MouseFilter = MouseFilterMode.Pass
|
||||
};
|
||||
|
||||
var bottomWrap = new LayoutContainer
|
||||
{
|
||||
Name = "BottomWrap"
|
||||
};
|
||||
|
||||
rootContainer.AddChild(topPanel);
|
||||
rootContainer.AddChild(bottomWrap);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(topPanel, LayoutContainer.LayoutPreset.Wide);
|
||||
LayoutContainer.SetMarginBottom(topPanel, -85);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(bottomWrap, LayoutContainer.LayoutPreset.VerticalCenterWide);
|
||||
LayoutContainer.SetGrowHorizontal(bottomWrap, LayoutContainer.GrowDirection.Both);
|
||||
|
||||
|
||||
var topContainerWrap = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
{
|
||||
Texture = panelTex,
|
||||
Modulate = Color.FromHex("#25252A"),
|
||||
};
|
||||
|
||||
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
|
||||
var topPanel = new PanelContainer
|
||||
{
|
||||
PanelOverride = back,
|
||||
MouseFilter = MouseFilterMode.Pass
|
||||
};
|
||||
|
||||
var bottomWrap = new LayoutContainer
|
||||
{
|
||||
Name = "BottomWrap"
|
||||
};
|
||||
|
||||
rootContainer.AddChild(topPanel);
|
||||
rootContainer.AddChild(bottomWrap);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(topPanel, LayoutContainer.LayoutPreset.Wide);
|
||||
LayoutContainer.SetMarginBottom(topPanel, -85);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(bottomWrap, LayoutContainer.LayoutPreset.VerticalCenterWide);
|
||||
LayoutContainer.SetGrowHorizontal(bottomWrap, LayoutContainer.GrowDirection.Both);
|
||||
|
||||
|
||||
var topContainerWrap = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
(topContainer = new BoxContainer
|
||||
{
|
||||
(_topContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
}),
|
||||
new Control {MinSize = new Vector2(0, 110)}
|
||||
}
|
||||
};
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
}),
|
||||
new Control {MinSize = new Vector2(0, 110)}
|
||||
}
|
||||
};
|
||||
|
||||
rootContainer.AddChild(topContainerWrap);
|
||||
rootContainer.AddChild(topContainerWrap);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(topContainerWrap, LayoutContainer.LayoutPreset.Wide);
|
||||
LayoutContainer.SetAnchorPreset(topContainerWrap, LayoutContainer.LayoutPreset.Wide);
|
||||
|
||||
var font = _resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13);
|
||||
var font = resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13);
|
||||
|
||||
var topRow = new BoxContainer
|
||||
var topRow = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Margin = new Thickness(4, 2, 12, 2),
|
||||
Children =
|
||||
{
|
||||
(new Label
|
||||
{
|
||||
Text = uidName,
|
||||
FontOverride = font,
|
||||
FontColorOverride = StyleNano.NanoGold,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
HorizontalAlignment = HAlignment.Left,
|
||||
Margin = new Thickness(0, 0, 20, 0),
|
||||
}),
|
||||
(btnClose = new TextureButton
|
||||
{
|
||||
StyleClasses = {DefaultWindow.StyleClassWindowCloseButton},
|
||||
VerticalAlignment = VAlignment.Center
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var middle = new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#202025") },
|
||||
Children =
|
||||
{
|
||||
(contentContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(8, 4),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
topContainer.AddChild(topRow);
|
||||
topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") }
|
||||
});
|
||||
topContainer.AddChild(middle);
|
||||
topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") }
|
||||
});
|
||||
|
||||
|
||||
_lblPressure = new RichTextLabel();
|
||||
contentContainer.AddChild(_lblPressure);
|
||||
|
||||
//internals
|
||||
_lblInternals = new RichTextLabel
|
||||
{ MinSize = new Vector2(200, 0), VerticalAlignment = VAlignment.Center };
|
||||
_btnInternals = new Button { Text = Loc.GetString("gas-tank-window-internals-toggle-button") };
|
||||
|
||||
contentContainer.AddChild(
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Margin = new Thickness(4, 2, 12, 2),
|
||||
Children =
|
||||
{
|
||||
(_lblName = new Label
|
||||
{
|
||||
Text = Loc.GetString("gas-tank-window-label"),
|
||||
FontOverride = font,
|
||||
FontColorOverride = StyleNano.NanoGold,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
HorizontalAlignment = HAlignment.Left,
|
||||
Margin = new Thickness(0, 0, 20, 0),
|
||||
}),
|
||||
(btnClose = new TextureButton
|
||||
{
|
||||
StyleClasses = {DefaultWindow.StyleClassWindowCloseButton},
|
||||
VerticalAlignment = VAlignment.Center
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var middle = new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#202025")},
|
||||
Children =
|
||||
{
|
||||
(_contentContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(8, 4),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
_topContainer.AddChild(topRow);
|
||||
_topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#525252ff")}
|
||||
});
|
||||
_topContainer.AddChild(middle);
|
||||
_topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#525252ff")}
|
||||
Margin = new Thickness(0, 7, 0, 0),
|
||||
Children = { _lblInternals, _btnInternals }
|
||||
});
|
||||
|
||||
|
||||
_lblPressure = new RichTextLabel();
|
||||
_contentContainer.AddChild(_lblPressure);
|
||||
|
||||
//internals
|
||||
_lblInternals = new RichTextLabel
|
||||
{MinSize = new Vector2(200, 0), VerticalAlignment = VAlignment.Center};
|
||||
_btnInternals = new Button {Text = Loc.GetString("gas-tank-window-internals-toggle-button") };
|
||||
|
||||
_contentContainer.AddChild(
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Margin = new Thickness(0, 7, 0, 0),
|
||||
Children = {_lblInternals, _btnInternals}
|
||||
});
|
||||
|
||||
// Separator
|
||||
_contentContainer.AddChild(new Control
|
||||
{
|
||||
MinSize = new Vector2(0, 10)
|
||||
});
|
||||
|
||||
_contentContainer.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("gas-tank-window-output-pressure-label"),
|
||||
Align = Label.AlignMode.Center
|
||||
});
|
||||
_spbPressure = new FloatSpinBox
|
||||
{
|
||||
IsValid = f => f >= 0 || f <= 3000,
|
||||
Margin = new Thickness(25, 0, 25, 7)
|
||||
};
|
||||
_contentContainer.AddChild(_spbPressure);
|
||||
|
||||
// Handlers
|
||||
_spbPressure.OnValueChanged += args =>
|
||||
{
|
||||
_owner.SetOutputPressure(args.Value);
|
||||
};
|
||||
|
||||
_btnInternals.OnPressed += args =>
|
||||
{
|
||||
_owner.ToggleInternals();
|
||||
};
|
||||
|
||||
btnClose.OnPressed += _ => Close();
|
||||
}
|
||||
|
||||
public void UpdateState(GasTankBoundUserInterfaceState state)
|
||||
// Separator
|
||||
contentContainer.AddChild(new Control
|
||||
{
|
||||
_lblPressure.SetMarkup(Loc.GetString("gas-tank-window-tank-pressure-text", ("tankPressure", $"{state.TankPressure:0.##}")));
|
||||
_btnInternals.Disabled = !state.CanConnectInternals;
|
||||
_lblInternals.SetMarkup(Loc.GetString("gas-tank-window-internal-text",
|
||||
("status", Loc.GetString(state.InternalsConnected ? "gas-tank-window-internal-connected" : "gas-tank-window-internal-disconnected"))));
|
||||
if (state.OutputPressure.HasValue)
|
||||
{
|
||||
_spbPressure.Value = state.OutputPressure.Value;
|
||||
}
|
||||
}
|
||||
MinSize = new Vector2(0, 10)
|
||||
});
|
||||
|
||||
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
|
||||
contentContainer.AddChild(new Label
|
||||
{
|
||||
return DragMode.Move;
|
||||
}
|
||||
Text = Loc.GetString("gas-tank-window-output-pressure-label"),
|
||||
Align = Label.AlignMode.Center
|
||||
});
|
||||
_spbPressure = new FloatSpinBox
|
||||
{
|
||||
IsValid = f => f >= 0 || f <= 3000,
|
||||
Margin = new Thickness(25, 0, 25, 7)
|
||||
};
|
||||
contentContainer.AddChild(_spbPressure);
|
||||
|
||||
protected override bool HasPoint(Vector2 point)
|
||||
// Handlers
|
||||
_spbPressure.OnValueChanged += args =>
|
||||
{
|
||||
return false;
|
||||
owner.SetOutputPressure(args.Value);
|
||||
};
|
||||
|
||||
_btnInternals.OnPressed += args =>
|
||||
{
|
||||
owner.ToggleInternals();
|
||||
};
|
||||
|
||||
btnClose.OnPressed += _ => Close();
|
||||
}
|
||||
|
||||
public void UpdateState(GasTankBoundUserInterfaceState state)
|
||||
{
|
||||
_lblPressure.SetMarkup(Loc.GetString("gas-tank-window-tank-pressure-text", ("tankPressure", $"{state.TankPressure:0.##}")));
|
||||
_btnInternals.Disabled = !state.CanConnectInternals;
|
||||
_lblInternals.SetMarkup(Loc.GetString("gas-tank-window-internal-text",
|
||||
("status", Loc.GetString(state.InternalsConnected ? "gas-tank-window-internal-connected" : "gas-tank-window-internal-disconnected"))));
|
||||
if (state.OutputPressure.HasValue)
|
||||
{
|
||||
_spbPressure.Value = state.OutputPressure.Value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
|
||||
{
|
||||
return DragMode.Move;
|
||||
}
|
||||
|
||||
protected override bool HasPoint(Vector2 point)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,15 @@ using Content.Client.Gameplay;
|
|||
using Content.Client.Guidebook;
|
||||
using Content.Client.Guidebook.Controls;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Guidebook;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Configuration;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -18,21 +23,25 @@ public sealed class GuidebookUIController : UIController, IOnStateEntered<LobbyS
|
|||
{
|
||||
[UISystemDependency] private readonly GuidebookSystem _guidebookSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
||||
[Dependency] private readonly JobRequirementsManager _jobRequirements = default!;
|
||||
|
||||
private const int PlaytimeOpenGuidebook = 60;
|
||||
|
||||
private GuidebookWindow? _guideWindow;
|
||||
private MenuButton? GuidebookButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.GuidebookButton;
|
||||
|
||||
public void OnStateEntered(LobbyState state)
|
||||
{
|
||||
HandleStateEntered();
|
||||
HandleStateEntered(state);
|
||||
}
|
||||
|
||||
public void OnStateEntered(GameplayState state)
|
||||
{
|
||||
HandleStateEntered();
|
||||
HandleStateEntered(state);
|
||||
}
|
||||
|
||||
private void HandleStateEntered()
|
||||
private void HandleStateEntered(State state)
|
||||
{
|
||||
DebugTools.Assert(_guideWindow == null);
|
||||
|
||||
|
|
@ -41,6 +50,14 @@ public sealed class GuidebookUIController : UIController, IOnStateEntered<LobbyS
|
|||
_guideWindow.OnClose += OnWindowClosed;
|
||||
_guideWindow.OnOpen += OnWindowOpen;
|
||||
|
||||
if (state is LobbyState &&
|
||||
_jobRequirements.FetchOverallPlaytime() < TimeSpan.FromMinutes(PlaytimeOpenGuidebook))
|
||||
{
|
||||
OpenGuidebook();
|
||||
_guideWindow.RecenterWindow(new(0.5f, 0.5f));
|
||||
_guideWindow.SetPositionFirst();
|
||||
}
|
||||
|
||||
// setup keybinding
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.OpenGuidebook,
|
||||
|
|
@ -74,12 +91,12 @@ public sealed class GuidebookUIController : UIController, IOnStateEntered<LobbyS
|
|||
|
||||
public void OnSystemLoaded(GuidebookSystem system)
|
||||
{
|
||||
_guidebookSystem.OnGuidebookOpen += ToggleGuidebook;
|
||||
_guidebookSystem.OnGuidebookOpen += OpenGuidebook;
|
||||
}
|
||||
|
||||
public void OnSystemUnloaded(GuidebookSystem system)
|
||||
{
|
||||
_guidebookSystem.OnGuidebookOpen -= ToggleGuidebook;
|
||||
_guidebookSystem.OnGuidebookOpen -= OpenGuidebook;
|
||||
}
|
||||
|
||||
internal void UnloadButton()
|
||||
|
|
@ -103,10 +120,29 @@ public sealed class GuidebookUIController : UIController, IOnStateEntered<LobbyS
|
|||
ToggleGuidebook();
|
||||
}
|
||||
|
||||
public void ToggleGuidebook()
|
||||
{
|
||||
if (_guideWindow == null)
|
||||
return;
|
||||
|
||||
if (_guideWindow.IsOpen)
|
||||
{
|
||||
UIManager.ClickSound();
|
||||
_guideWindow.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
OpenGuidebook();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (GuidebookButton != null)
|
||||
GuidebookButton.Pressed = false;
|
||||
|
||||
if (_guideWindow != null)
|
||||
_guideWindow.ReturnContainer.Visible = false;
|
||||
}
|
||||
|
||||
private void OnWindowOpen()
|
||||
|
|
@ -127,30 +163,25 @@ public sealed class GuidebookUIController : UIController, IOnStateEntered<LobbyS
|
|||
/// <param name="includeChildren">Whether or not to automatically include child entries. If false, this will ONLY
|
||||
/// show the specified entries</param>
|
||||
/// <param name="selected">The guide whose contents should be displayed when the guidebook is opened</param>
|
||||
public void ToggleGuidebook(
|
||||
Dictionary<string, GuideEntry>? guides = null,
|
||||
List<string>? rootEntries = null,
|
||||
string? forceRoot = null,
|
||||
public void OpenGuidebook(
|
||||
Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry>? guides = null,
|
||||
List<ProtoId<GuideEntryPrototype>>? rootEntries = null,
|
||||
ProtoId<GuideEntryPrototype>? forceRoot = null,
|
||||
bool includeChildren = true,
|
||||
string? selected = null)
|
||||
ProtoId<GuideEntryPrototype>? selected = null)
|
||||
{
|
||||
if (_guideWindow == null)
|
||||
return;
|
||||
|
||||
if (_guideWindow.IsOpen)
|
||||
{
|
||||
UIManager.ClickSound();
|
||||
_guideWindow.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (GuidebookButton != null)
|
||||
GuidebookButton.SetClickPressed(!_guideWindow.IsOpen);
|
||||
|
||||
selected ??= _configuration.GetCVar(CCVars.DefaultGuide);
|
||||
|
||||
if (guides == null)
|
||||
{
|
||||
guides = _prototypeManager.EnumeratePrototypes<GuideEntryPrototype>()
|
||||
.ToDictionary(x => x.ID, x => (GuideEntry) x);
|
||||
.ToDictionary(x => new ProtoId<GuideEntryPrototype>(x.ID), x => (GuideEntry) x);
|
||||
}
|
||||
else if (includeChildren)
|
||||
{
|
||||
|
|
@ -171,17 +202,17 @@ public sealed class GuidebookUIController : UIController, IOnStateEntered<LobbyS
|
|||
_guideWindow.OpenCenteredRight();
|
||||
}
|
||||
|
||||
public void ToggleGuidebook(
|
||||
List<string> guideList,
|
||||
List<string>? rootEntries = null,
|
||||
string? forceRoot = null,
|
||||
public void OpenGuidebook(
|
||||
List<ProtoId<GuideEntryPrototype>> guideList,
|
||||
List<ProtoId<GuideEntryPrototype>>? rootEntries = null,
|
||||
ProtoId<GuideEntryPrototype>? forceRoot = null,
|
||||
bool includeChildren = true,
|
||||
string? selected = null)
|
||||
ProtoId<GuideEntryPrototype>? selected = null)
|
||||
{
|
||||
Dictionary<string, GuideEntry> guides = new();
|
||||
Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry> guides = new();
|
||||
foreach (var guideId in guideList)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<GuideEntryPrototype>(guideId, out var guide))
|
||||
if (!_prototypeManager.TryIndex(guideId, out var guide))
|
||||
{
|
||||
Logger.Error($"Encountered unknown guide prototype: {guideId}");
|
||||
continue;
|
||||
|
|
@ -189,17 +220,29 @@ public sealed class GuidebookUIController : UIController, IOnStateEntered<LobbyS
|
|||
guides.Add(guideId, guide);
|
||||
}
|
||||
|
||||
ToggleGuidebook(guides, rootEntries, forceRoot, includeChildren, selected);
|
||||
OpenGuidebook(guides, rootEntries, forceRoot, includeChildren, selected);
|
||||
}
|
||||
|
||||
private void RecursivelyAddChildren(GuideEntry guide, Dictionary<string, GuideEntry> guides)
|
||||
public void CloseGuidebook()
|
||||
{
|
||||
if (_guideWindow == null)
|
||||
return;
|
||||
|
||||
if (_guideWindow.IsOpen)
|
||||
{
|
||||
UIManager.ClickSound();
|
||||
_guideWindow.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void RecursivelyAddChildren(GuideEntry guide, Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry> guides)
|
||||
{
|
||||
foreach (var childId in guide.Children)
|
||||
{
|
||||
if (guides.ContainsKey(childId))
|
||||
continue;
|
||||
|
||||
if (!_prototypeManager.TryIndex<GuideEntryPrototype>(childId, out var child))
|
||||
if (!_prototypeManager.TryIndex(childId, out var child))
|
||||
{
|
||||
Logger.Error($"Encountered unknown guide prototype: {childId} as a child of {guide.Id}. If the child is not a prototype, it must be directly provided.");
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,49 @@
|
|||
using Content.Client.Gameplay;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Info;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Guidebook;
|
||||
using Content.Shared.Info;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Info;
|
||||
|
||||
public sealed class InfoUIController : UIController, IOnStateExited<GameplayState>
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
private RulesPopup? _rulesPopup;
|
||||
private RulesAndInfoWindow? _infoWindow;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
|
||||
_netManager.RegisterNetMessage<RulesAcceptedMessage>();
|
||||
_netManager.RegisterNetMessage<ShowRulesPopupMessage>(OnShowRulesPopupMessage);
|
||||
|
||||
_consoleHost.RegisterCommand("fuckrules",
|
||||
"",
|
||||
"",
|
||||
(_, _, _) =>
|
||||
{
|
||||
OnAcceptPressed();
|
||||
});
|
||||
}
|
||||
|
||||
private void OnShowRulesPopupMessage(ShowRulesPopupMessage message)
|
||||
{
|
||||
ShowRules(message.PopupTime);
|
||||
}
|
||||
|
||||
public void OnStateExited(GameplayState state)
|
||||
{
|
||||
if (_infoWindow == null)
|
||||
|
|
@ -17,12 +53,46 @@ public sealed class InfoUIController : UIController, IOnStateExited<GameplayStat
|
|||
_infoWindow = null;
|
||||
}
|
||||
|
||||
private void ShowRules(float time)
|
||||
{
|
||||
if (_rulesPopup != null)
|
||||
return;
|
||||
|
||||
_rulesPopup = new RulesPopup
|
||||
{
|
||||
Timer = time
|
||||
};
|
||||
|
||||
_rulesPopup.OnQuitPressed += OnQuitPressed;
|
||||
_rulesPopup.OnAcceptPressed += OnAcceptPressed;
|
||||
UIManager.WindowRoot.AddChild(_rulesPopup);
|
||||
LayoutContainer.SetAnchorPreset(_rulesPopup, LayoutContainer.LayoutPreset.Wide);
|
||||
}
|
||||
|
||||
private void OnQuitPressed()
|
||||
{
|
||||
_consoleHost.ExecuteCommand("quit");
|
||||
}
|
||||
|
||||
private void OnAcceptPressed()
|
||||
{
|
||||
_netManager.ClientSendMessage(new RulesAcceptedMessage());
|
||||
|
||||
_rulesPopup?.Orphan();
|
||||
_rulesPopup = null;
|
||||
}
|
||||
|
||||
public GuideEntryPrototype GetCoreRuleEntry()
|
||||
{
|
||||
var guide = _cfg.GetCVar(CCVars.RulesFile);
|
||||
var guideEntryPrototype = _prototype.Index<GuideEntryPrototype>(guide);
|
||||
return guideEntryPrototype;
|
||||
}
|
||||
|
||||
public void OpenWindow()
|
||||
{
|
||||
if (_infoWindow == null || _infoWindow.Disposed)
|
||||
{
|
||||
_infoWindow = UIManager.CreateWindow<RulesAndInfoWindow>();
|
||||
}
|
||||
|
||||
_infoWindow?.OpenCentered();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,40 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Zombies;
|
||||
|
||||
public sealed class ZombieSystem : EntitySystem
|
||||
public sealed class ZombieSystem : SharedZombieSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ZombieComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<ZombieComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons);
|
||||
SubscribeLocalEvent<InitialInfectedComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons);
|
||||
SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(GetZombieIcon);
|
||||
SubscribeLocalEvent<InitialInfectedComponent, GetStatusIconsEvent>(GetInitialInfectedIcon);
|
||||
}
|
||||
|
||||
private void GetZombieIcon(Entity<ZombieComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
var iconPrototype = _prototype.Index(ent.Comp.StatusIcon);
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
private void GetInitialInfectedIcon(Entity<InitialInfectedComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<ZombieComponent>(ent))
|
||||
return;
|
||||
|
||||
var iconPrototype = _prototype.Index(ent.Comp.StatusIcon);
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartup args)
|
||||
|
|
@ -31,29 +50,4 @@ public sealed class ZombieSystem : EntitySystem
|
|||
sprite.LayerSetColor(i, component.SkinColor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a player should be able to see the StatusIcon for zombies.
|
||||
/// </summary>
|
||||
private void OnCanDisplayStatusIcons(EntityUid uid, ZombieComponent component, ref CanDisplayStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<ZombieComponent>(args.User) || HasComp<InitialInfectedComponent>(args.User) || HasComp<ShowZombieIconsComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (component.IconVisibleToGhost && HasComp<GhostComponent>(args.User))
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnCanDisplayStatusIcons(EntityUid uid, InitialInfectedComponent component, ref CanDisplayStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<InitialInfectedComponent>(args.User) && !HasComp<ZombieComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (component.IconVisibleToGhost && HasComp<GhostComponent>(args.User))
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Content.Shared.Preferences;
|
|||
using Content.Shared.Roles;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.UnitTesting;
|
||||
|
||||
|
|
@ -133,27 +134,73 @@ public sealed partial class TestPair
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for enabling or disabling a antag role
|
||||
/// Set a user's antag preferences. Modified preferences are automatically reset at the end of the test.
|
||||
/// </summary>
|
||||
public async Task SetAntagPref(ProtoId<AntagPrototype> id, bool value)
|
||||
public async Task SetAntagPreference(ProtoId<AntagPrototype> id, bool value, NetUserId? user = null)
|
||||
{
|
||||
user ??= Client.User!.Value;
|
||||
if (user is not {} userId)
|
||||
return;
|
||||
|
||||
var prefMan = Server.ResolveDependency<IServerPreferencesManager>();
|
||||
var prefs = prefMan.GetPreferences(userId);
|
||||
|
||||
var prefs = prefMan.GetPreferences(Client.User!.Value);
|
||||
// what even is the point of ICharacterProfile if we always cast it to HumanoidCharacterProfile to make it usable?
|
||||
var profile = (HumanoidCharacterProfile) prefs.SelectedCharacter;
|
||||
// Automatic preference resetting only resets slot 0.
|
||||
Assert.That(prefs.SelectedCharacterIndex, Is.EqualTo(0));
|
||||
|
||||
Assert.That(profile.AntagPreferences.Contains(id), Is.EqualTo(!value));
|
||||
var profile = (HumanoidCharacterProfile) prefs.Characters[0];
|
||||
var newProfile = profile.WithAntagPreference(id, value);
|
||||
_modifiedProfiles.Add(userId);
|
||||
await Server.WaitPost(() => prefMan.SetProfile(userId, 0, newProfile).Wait());
|
||||
}
|
||||
|
||||
await Server.WaitPost(() =>
|
||||
/// <summary>
|
||||
/// Set a user's job preferences. Modified preferences are automatically reset at the end of the test.
|
||||
/// </summary>
|
||||
public async Task SetJobPriority(ProtoId<JobPrototype> id, JobPriority value, NetUserId? user = null)
|
||||
{
|
||||
user ??= Client.User!.Value;
|
||||
if (user is { } userId)
|
||||
await SetJobPriorities(userId, (id, value));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="SetJobPriority"/>
|
||||
public async Task SetJobPriorities(params (ProtoId<JobPrototype>, JobPriority)[] priorities)
|
||||
=> await SetJobPriorities(Client.User!.Value, priorities);
|
||||
|
||||
/// <inheritdoc cref="SetJobPriority"/>
|
||||
public async Task SetJobPriorities(NetUserId user, params (ProtoId<JobPrototype>, JobPriority)[] priorities)
|
||||
{
|
||||
var highCount = priorities.Count(x => x.Item2 == JobPriority.High);
|
||||
Assert.That(highCount, Is.LessThanOrEqualTo(1), "Cannot have more than one high priority job");
|
||||
|
||||
var prefMan = Server.ResolveDependency<IServerPreferencesManager>();
|
||||
var prefs = prefMan.GetPreferences(user);
|
||||
var profile = (HumanoidCharacterProfile) prefs.Characters[0];
|
||||
var dictionary = new Dictionary<ProtoId<JobPrototype>, JobPriority>(profile.JobPriorities);
|
||||
|
||||
// Automatic preference resetting only resets slot 0.
|
||||
Assert.That(prefs.SelectedCharacterIndex, Is.EqualTo(0));
|
||||
|
||||
if (highCount != 0)
|
||||
{
|
||||
prefMan.SetProfile(Client.User.Value, prefs.SelectedCharacterIndex, newProfile).Wait();
|
||||
});
|
||||
foreach (var (key, priority) in dictionary)
|
||||
{
|
||||
if (priority == JobPriority.High)
|
||||
dictionary[key] = JobPriority.Medium;
|
||||
}
|
||||
}
|
||||
|
||||
// And why the fuck does it always create a new preference and profile object instead of just reusing them?
|
||||
var newPrefs = prefMan.GetPreferences(Client.User.Value);
|
||||
var newProf = (HumanoidCharacterProfile) newPrefs.SelectedCharacter;
|
||||
Assert.That(newProf.AntagPreferences.Contains(id), Is.EqualTo(value));
|
||||
foreach (var (job, priority) in priorities)
|
||||
{
|
||||
if (priority == JobPriority.Never)
|
||||
dictionary.Remove(job);
|
||||
else
|
||||
dictionary[job] = priority;
|
||||
}
|
||||
|
||||
var newProfile = profile.WithJobPriorities(dictionary);
|
||||
_modifiedProfiles.Add(user);
|
||||
await Server.WaitPost(() => prefMan.SetProfile(user, 0, newProfile).Wait());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Exceptions;
|
||||
|
|
@ -34,6 +36,11 @@ public sealed partial class TestPair : IAsyncDisposable
|
|||
|
||||
private async Task OnCleanDispose()
|
||||
{
|
||||
await Server.WaitIdleAsync();
|
||||
await Client.WaitIdleAsync();
|
||||
await ResetModifiedPreferences();
|
||||
await Server.RemoveAllDummySessions();
|
||||
|
||||
if (TestMap != null)
|
||||
{
|
||||
await Server.WaitPost(() => Server.EntMan.DeleteEntity(TestMap.MapUid));
|
||||
|
|
@ -79,6 +86,16 @@ public sealed partial class TestPair : IAsyncDisposable
|
|||
await _testOut.WriteLineAsync($"{nameof(CleanReturnAsync)}: PoolManager took {returnTime.TotalMilliseconds} ms to put pair {Id} back into the pool");
|
||||
}
|
||||
|
||||
private async Task ResetModifiedPreferences()
|
||||
{
|
||||
var prefMan = Server.ResolveDependency<IServerPreferencesManager>();
|
||||
foreach (var user in _modifiedProfiles)
|
||||
{
|
||||
await Server.WaitPost(() => prefMan.SetProfile(user, 0, new HumanoidCharacterProfile()).Wait());
|
||||
}
|
||||
_modifiedProfiles.Clear();
|
||||
}
|
||||
|
||||
public async ValueTask CleanReturnAsync()
|
||||
{
|
||||
if (State != PairState.InUse)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ public sealed partial class TestPair
|
|||
public readonly List<string> TestHistory = new();
|
||||
public PoolSettings Settings = default!;
|
||||
public TestMapData? TestMap;
|
||||
private List<NetUserId> _modifiedProfiles = new();
|
||||
|
||||
public RobustIntegrationTest.ServerIntegrationInstance Server { get; private set; } = default!;
|
||||
public RobustIntegrationTest.ClientIntegrationInstance Client { get; private set; } = default!;
|
||||
|
||||
|
|
@ -37,7 +39,8 @@ public sealed partial class TestPair
|
|||
client = Client;
|
||||
}
|
||||
|
||||
public ICommonSession? Player => Server.PlayerMan.Sessions.FirstOrDefault();
|
||||
public ICommonSession? Player => Server.PlayerMan.SessionsDict.GetValueOrDefault(Client.User!.Value);
|
||||
|
||||
public ContentPlayerData? PlayerData => Player?.Data.ContentData();
|
||||
|
||||
public PoolTestLogHandler ServerLogHandler { get; private set; } = default!;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public static partial class PoolManager
|
|||
(CCVars.EmergencyShuttleEnabled.Name, "false"),
|
||||
(CCVars.ProcgenPreload.Name, "false"),
|
||||
(CCVars.WorldgenEnabled.Name, "false"),
|
||||
(CCVars.GatewayGeneratorEnabled.Name, "false"),
|
||||
(CVars.ReplayClientRecordingEnabled.Name, "false"),
|
||||
(CVars.ReplayServerRecordingEnabled.Name, "false"),
|
||||
(CCVars.GameDummyTicker.Name, "true"),
|
||||
|
|
|
|||
|
|
@ -3,8 +3,13 @@ using System.Linq;
|
|||
using System.Numerics;
|
||||
using Content.Server.Cargo.Components;
|
||||
using Content.Server.Cargo.Systems;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Server.Nutrition.EntitySystems;
|
||||
using Content.Shared.Cargo.Prototypes;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -149,6 +154,80 @@ public sealed class CargoTest
|
|||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests to see if any items that are valid for cargo bounties can be sliced into items that
|
||||
/// are also valid for the same bounty entry.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task NoSliceableBountyArbitrageTest()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var testMap = await pair.CreateTestMap();
|
||||
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||
var componentFactory = server.ResolveDependency<IComponentFactory>();
|
||||
var whitelist = entManager.System<EntityWhitelistSystem>();
|
||||
var cargo = entManager.System<CargoSystem>();
|
||||
var sliceableSys = entManager.System<SliceableFoodSystem>();
|
||||
|
||||
var bounties = protoManager.EnumeratePrototypes<CargoBountyPrototype>().ToList();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var mapId = testMap.MapId;
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
var coord = new EntityCoordinates(grid.Owner, 0, 0);
|
||||
|
||||
var sliceableEntityProtos = protoManager.EnumeratePrototypes<EntityPrototype>()
|
||||
.Where(p => !p.Abstract)
|
||||
.Where(p => !pair.IsTestPrototype(p))
|
||||
.Where(p => p.TryGetComponent<SliceableFoodComponent>(out _, componentFactory))
|
||||
.Select(p => p.ID)
|
||||
.ToList();
|
||||
|
||||
foreach (var proto in sliceableEntityProtos)
|
||||
{
|
||||
var ent = entManager.SpawnEntity(proto, coord);
|
||||
var sliceable = entManager.GetComponent<SliceableFoodComponent>(ent);
|
||||
|
||||
// Check each bounty
|
||||
foreach (var bounty in bounties)
|
||||
{
|
||||
// Check each entry in the bounty
|
||||
foreach (var entry in bounty.Entries)
|
||||
{
|
||||
// See if the entity counts as part of this bounty entry
|
||||
if (!cargo.IsValidBountyEntry(ent, entry))
|
||||
continue;
|
||||
|
||||
// Spawn a slice
|
||||
var slice = entManager.SpawnEntity(sliceable.Slice, coord);
|
||||
|
||||
// See if the slice also counts for this bounty entry
|
||||
if (!cargo.IsValidBountyEntry(slice, entry))
|
||||
{
|
||||
entManager.DeleteEntity(slice);
|
||||
continue;
|
||||
}
|
||||
|
||||
entManager.DeleteEntity(slice);
|
||||
|
||||
// If for some reason it can only make one slice, that's okay, I guess
|
||||
Assert.That(sliceable.TotalCount, Is.EqualTo(1), $"{proto} counts as part of cargo bounty {bounty.ID} and slices into {sliceable.TotalCount} slices which count for the same bounty!");
|
||||
}
|
||||
}
|
||||
|
||||
entManager.DeleteEntity(ent);
|
||||
}
|
||||
mapManager.DeleteMap(mapId);
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
[TestPrototypes]
|
||||
private const string StackProto = @"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public sealed class DispenserTest : InteractionTest
|
|||
ToggleNeedPower();
|
||||
|
||||
// Insert beaker
|
||||
await Interact("Beaker");
|
||||
await InteractUsing("Beaker");
|
||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
||||
|
||||
// Open BUI
|
||||
|
|
|
|||
|
|
@ -16,10 +16,8 @@ public sealed class ComputerConstruction : InteractionTest
|
|||
await StartConstruction(Computer);
|
||||
|
||||
// Initial interaction (ghost turns into real entity)
|
||||
await Interact(Steel, 5);
|
||||
ClientAssertPrototype(ComputerFrame, ClientTarget);
|
||||
Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()];
|
||||
ClientTarget = null;
|
||||
await InteractUsing(Steel, 5);
|
||||
ClientAssertPrototype(ComputerFrame, Target);
|
||||
|
||||
// Perform construction steps
|
||||
await Interact(
|
||||
|
|
@ -41,7 +39,7 @@ public sealed class ComputerConstruction : InteractionTest
|
|||
await StartDeconstruction(ComputerId);
|
||||
|
||||
// Initial interaction turns id computer into generic computer
|
||||
await Interact(Screw);
|
||||
await InteractUsing(Screw);
|
||||
AssertPrototype(ComputerFrame);
|
||||
|
||||
// Perform deconstruction steps
|
||||
|
|
@ -71,7 +69,7 @@ public sealed class ComputerConstruction : InteractionTest
|
|||
await SpawnTarget(ComputerId);
|
||||
|
||||
// Initial interaction turns id computer into generic computer
|
||||
await Interact(Screw);
|
||||
await InteractUsing(Screw);
|
||||
AssertPrototype(ComputerFrame);
|
||||
|
||||
// Perform partial deconstruction steps
|
||||
|
|
|
|||
|
|
@ -17,17 +17,14 @@ public sealed class GrilleWindowConstruction : InteractionTest
|
|||
{
|
||||
// Construct Grille
|
||||
await StartConstruction(Grille);
|
||||
await Interact(Rod, 10);
|
||||
ClientAssertPrototype(Grille, ClientTarget);
|
||||
|
||||
Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()];
|
||||
await InteractUsing(Rod, 10);
|
||||
ClientAssertPrototype(Grille, Target);
|
||||
var grille = Target;
|
||||
|
||||
// Construct Window
|
||||
await StartConstruction(Window);
|
||||
await Interact(Glass, 10);
|
||||
ClientAssertPrototype(Window, ClientTarget);
|
||||
Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()];
|
||||
await InteractUsing(Glass, 10);
|
||||
ClientAssertPrototype(Window, Target);
|
||||
|
||||
// Deconstruct Window
|
||||
await Interact(Screw, Wrench);
|
||||
|
|
@ -35,7 +32,7 @@ public sealed class GrilleWindowConstruction : InteractionTest
|
|||
|
||||
// Deconstruct Grille
|
||||
Target = grille;
|
||||
await Interact(Cut);
|
||||
await InteractUsing(Cut);
|
||||
AssertDeleted();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,8 @@ public sealed class MachineConstruction : InteractionTest
|
|||
public async Task ConstructProtolathe()
|
||||
{
|
||||
await StartConstruction(MachineFrame);
|
||||
await Interact(Steel, 5);
|
||||
ClientAssertPrototype(Unfinished, ClientTarget);
|
||||
Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()];
|
||||
await InteractUsing(Steel, 5);
|
||||
ClientAssertPrototype(Unfinished, Target);
|
||||
await Interact(Wrench, Cable);
|
||||
AssertPrototype(MachineFrame);
|
||||
await Interact(ProtolatheBoard, Bin1, Bin1, Manipulator1, Manipulator1, Beaker, Beaker, Screw);
|
||||
|
|
@ -51,7 +50,7 @@ public sealed class MachineConstruction : InteractionTest
|
|||
AssertPrototype(MachineFrame);
|
||||
|
||||
// Change it into an autolathe
|
||||
await Interact("AutolatheMachineCircuitboard");
|
||||
await InteractUsing("AutolatheMachineCircuitboard");
|
||||
AssertPrototype(MachineFrame);
|
||||
await Interact(Bin1, Bin1, Bin1, Manipulator1, Glass, Screw);
|
||||
AssertPrototype("Autolathe");
|
||||
|
|
|
|||
|
|
@ -19,21 +19,21 @@ public sealed class PanelScrewing : InteractionTest
|
|||
|
||||
// Open & close panel
|
||||
Assert.That(comp.Open, Is.False);
|
||||
await Interact(Screw);
|
||||
await InteractUsing(Screw);
|
||||
Assert.That(comp.Open, Is.True);
|
||||
await Interact(Screw);
|
||||
await InteractUsing(Screw);
|
||||
Assert.That(comp.Open, Is.False);
|
||||
|
||||
// Interrupted DoAfters
|
||||
await Interact(Screw, awaitDoAfters: false);
|
||||
await InteractUsing(Screw, awaitDoAfters: false);
|
||||
await CancelDoAfters();
|
||||
Assert.That(comp.Open, Is.False);
|
||||
await Interact(Screw);
|
||||
await InteractUsing(Screw);
|
||||
Assert.That(comp.Open, Is.True);
|
||||
await Interact(Screw, awaitDoAfters: false);
|
||||
await InteractUsing(Screw, awaitDoAfters: false);
|
||||
await CancelDoAfters();
|
||||
Assert.That(comp.Open, Is.True);
|
||||
await Interact(Screw);
|
||||
await InteractUsing(Screw);
|
||||
Assert.That(comp.Open, Is.False);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ public sealed class PlaceableDeconstruction : InteractionTest
|
|||
{
|
||||
await StartDeconstruction("Table");
|
||||
Assert.That(Comp<PlaceableSurfaceComponent>().IsPlaceable);
|
||||
await Interact(Wrench);
|
||||
await InteractUsing(Wrench);
|
||||
AssertPrototype("TableFrame");
|
||||
await Interact(Wrench);
|
||||
await InteractUsing(Wrench);
|
||||
AssertDeleted();
|
||||
await AssertEntityLookup((Steel, 1), (Rod, 2));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,10 @@ public sealed class WallConstruction : InteractionTest
|
|||
public async Task ConstructWall()
|
||||
{
|
||||
await StartConstruction(Wall);
|
||||
await Interact(Steel, 2);
|
||||
await InteractUsing(Steel, 2);
|
||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
||||
ClientAssertPrototype(Girder, ClientTarget);
|
||||
Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()];
|
||||
await Interact(Steel, 2);
|
||||
ClientAssertPrototype(Girder, Target);
|
||||
await InteractUsing(Steel, 2);
|
||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
||||
AssertPrototype(WallSolid);
|
||||
}
|
||||
|
|
@ -25,7 +24,7 @@ public sealed class WallConstruction : InteractionTest
|
|||
public async Task DeconstructWall()
|
||||
{
|
||||
await StartDeconstruction(WallSolid);
|
||||
await Interact(Weld);
|
||||
await InteractUsing(Weld);
|
||||
AssertPrototype(Girder);
|
||||
await Interact(Wrench, Screw);
|
||||
AssertDeleted();
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ public sealed class WindowConstruction : InteractionTest
|
|||
public async Task ConstructWindow()
|
||||
{
|
||||
await StartConstruction(Window);
|
||||
await Interact(Glass, 5);
|
||||
ClientAssertPrototype(Window, ClientTarget);
|
||||
await InteractUsing(Glass, 5);
|
||||
ClientAssertPrototype(Window, Target);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
@ -28,8 +28,8 @@ public sealed class WindowConstruction : InteractionTest
|
|||
public async Task ConstructReinforcedWindow()
|
||||
{
|
||||
await StartConstruction(RWindow);
|
||||
await Interact(RGlass, 5);
|
||||
ClientAssertPrototype(RWindow, ClientTarget);
|
||||
await InteractUsing(RGlass, 5);
|
||||
ClientAssertPrototype(RWindow, Target);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public sealed class WindowRepair : InteractionTest
|
|||
Assert.That(comp.Damage.GetTotal(), Is.GreaterThan(FixedPoint2.Zero));
|
||||
|
||||
// Repair the entity
|
||||
await Interact(Weld);
|
||||
await InteractUsing(Weld);
|
||||
Assert.That(comp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero));
|
||||
|
||||
// Validate that we can still deconstruct the entity (i.e., that welding deconstruction is not blocked).
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Content.Server.Destructible.Thresholds;
|
|||
using Content.Server.Destructible.Thresholds.Behaviors;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Destructible.Thresholds;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||
|
|
|
|||
|
|
@ -16,31 +16,31 @@ public sealed class DoAfterCancellationTests : InteractionTest
|
|||
public async Task CancelWallDeconstruct()
|
||||
{
|
||||
await StartDeconstruction(WallConstruction.WallSolid);
|
||||
await Interact(Weld, awaitDoAfters: false);
|
||||
await InteractUsing(Weld, awaitDoAfters: false);
|
||||
|
||||
// Failed do-after has no effect
|
||||
await CancelDoAfters();
|
||||
AssertPrototype(WallConstruction.WallSolid);
|
||||
|
||||
// Second attempt works fine
|
||||
await Interact(Weld);
|
||||
await InteractUsing(Weld);
|
||||
AssertPrototype(WallConstruction.Girder);
|
||||
|
||||
// Repeat for wrenching interaction
|
||||
AssertAnchored();
|
||||
await Interact(Wrench, awaitDoAfters: false);
|
||||
await InteractUsing(Wrench, awaitDoAfters: false);
|
||||
await CancelDoAfters();
|
||||
AssertAnchored();
|
||||
AssertPrototype(WallConstruction.Girder);
|
||||
await Interact(Wrench);
|
||||
await InteractUsing(Wrench);
|
||||
AssertAnchored(false);
|
||||
|
||||
// Repeat for screwdriver interaction.
|
||||
AssertExists();
|
||||
await Interact(Screw, awaitDoAfters: false);
|
||||
await InteractUsing(Screw, awaitDoAfters: false);
|
||||
await CancelDoAfters();
|
||||
AssertExists();
|
||||
await Interact(Screw);
|
||||
await InteractUsing(Screw);
|
||||
AssertDeleted();
|
||||
}
|
||||
|
||||
|
|
@ -48,17 +48,16 @@ public sealed class DoAfterCancellationTests : InteractionTest
|
|||
public async Task CancelWallConstruct()
|
||||
{
|
||||
await StartConstruction(WallConstruction.Wall);
|
||||
await Interact(Steel, 5, awaitDoAfters: false);
|
||||
await InteractUsing(Steel, 5, awaitDoAfters: false);
|
||||
await CancelDoAfters();
|
||||
|
||||
await Interact(Steel, 5);
|
||||
ClientAssertPrototype(WallConstruction.Girder, ClientTarget);
|
||||
Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()];
|
||||
await Interact(Steel, 5, awaitDoAfters: false);
|
||||
await InteractUsing(Steel, 5);
|
||||
ClientAssertPrototype(WallConstruction.Girder, Target);
|
||||
await InteractUsing(Steel, 5, awaitDoAfters: false);
|
||||
await CancelDoAfters();
|
||||
AssertPrototype(WallConstruction.Girder);
|
||||
|
||||
await Interact(Steel, 5);
|
||||
await InteractUsing(Steel, 5);
|
||||
AssertPrototype(WallConstruction.WallSolid);
|
||||
}
|
||||
|
||||
|
|
@ -66,11 +65,11 @@ public sealed class DoAfterCancellationTests : InteractionTest
|
|||
public async Task CancelTilePry()
|
||||
{
|
||||
await SetTile(Floor);
|
||||
await Interact(Pry, awaitDoAfters: false);
|
||||
await InteractUsing(Pry, awaitDoAfters: false);
|
||||
await CancelDoAfters();
|
||||
await AssertTile(Floor);
|
||||
|
||||
await Interact(Pry);
|
||||
await InteractUsing(Pry);
|
||||
await AssertTile(Plating);
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +77,7 @@ public sealed class DoAfterCancellationTests : InteractionTest
|
|||
public async Task CancelRepeatedTilePry()
|
||||
{
|
||||
await SetTile(Floor);
|
||||
await Interact(Pry, awaitDoAfters: false);
|
||||
await InteractUsing(Pry, awaitDoAfters: false);
|
||||
await RunTicks(1);
|
||||
Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1));
|
||||
await AssertTile(Floor);
|
||||
|
|
@ -89,7 +88,7 @@ public sealed class DoAfterCancellationTests : InteractionTest
|
|||
await AssertTile(Floor);
|
||||
|
||||
// Third do after will work fine
|
||||
await Interact(Pry);
|
||||
await InteractUsing(Pry);
|
||||
Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0));
|
||||
await AssertTile(Plating);
|
||||
}
|
||||
|
|
@ -102,7 +101,7 @@ public sealed class DoAfterCancellationTests : InteractionTest
|
|||
|
||||
Assert.That(comp.IsWelded, Is.False);
|
||||
|
||||
await Interact(Weld, awaitDoAfters: false);
|
||||
await InteractUsing(Weld, awaitDoAfters: false);
|
||||
await RunTicks(1);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
|
|
@ -120,7 +119,7 @@ public sealed class DoAfterCancellationTests : InteractionTest
|
|||
});
|
||||
|
||||
// Third do after will work fine
|
||||
await Interact(Weld);
|
||||
await InteractUsing(Weld);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0));
|
||||
|
|
@ -128,7 +127,7 @@ public sealed class DoAfterCancellationTests : InteractionTest
|
|||
});
|
||||
|
||||
// Repeat test for un-welding
|
||||
await Interact(Weld, awaitDoAfters: false);
|
||||
await InteractUsing(Weld, awaitDoAfters: false);
|
||||
await RunTicks(1);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
|
|
@ -141,7 +140,7 @@ public sealed class DoAfterCancellationTests : InteractionTest
|
|||
Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0));
|
||||
Assert.That(comp.IsWelded, Is.True);
|
||||
});
|
||||
await Interact(Weld);
|
||||
await InteractUsing(Weld);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0));
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public sealed class RemoveEncryptionKeys : InteractionTest
|
|||
});
|
||||
|
||||
// Remove the key
|
||||
await Interact(Screw);
|
||||
await InteractUsing(Screw);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(0));
|
||||
|
|
@ -34,7 +34,7 @@ public sealed class RemoveEncryptionKeys : InteractionTest
|
|||
await AssertEntityLookup(("EncryptionKeyCommon", 1));
|
||||
|
||||
// Re-insert a key.
|
||||
await Interact("EncryptionKeyCentCom");
|
||||
await InteractUsing("EncryptionKeyCentCom");
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(1));
|
||||
|
|
@ -59,7 +59,7 @@ public sealed class RemoveEncryptionKeys : InteractionTest
|
|||
});
|
||||
|
||||
// cannot remove keys without opening panel
|
||||
await Interact(Pry);
|
||||
await InteractUsing(Pry);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.GreaterThan(0));
|
||||
|
|
@ -68,7 +68,7 @@ public sealed class RemoveEncryptionKeys : InteractionTest
|
|||
});
|
||||
|
||||
// Open panel
|
||||
await Interact(Screw);
|
||||
await InteractUsing(Screw);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(panel.Open, Is.True);
|
||||
|
|
@ -79,7 +79,7 @@ public sealed class RemoveEncryptionKeys : InteractionTest
|
|||
});
|
||||
|
||||
// Now remove the keys
|
||||
await Interact(Pry);
|
||||
await InteractUsing(Pry);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(0));
|
||||
|
|
@ -87,7 +87,7 @@ public sealed class RemoveEncryptionKeys : InteractionTest
|
|||
});
|
||||
|
||||
// Reinsert a key
|
||||
await Interact("EncryptionKeyCentCom");
|
||||
await InteractUsing("EncryptionKeyCentCom");
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(1));
|
||||
|
|
@ -97,7 +97,7 @@ public sealed class RemoveEncryptionKeys : InteractionTest
|
|||
});
|
||||
|
||||
// Remove it again
|
||||
await Interact(Pry);
|
||||
await InteractUsing(Pry);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(0));
|
||||
|
|
@ -106,7 +106,7 @@ public sealed class RemoveEncryptionKeys : InteractionTest
|
|||
|
||||
// Prying again will start deconstructing the machine.
|
||||
AssertPrototype("TelecomServerFilled");
|
||||
await Interact(Pry);
|
||||
await InteractUsing(Pry);
|
||||
AssertPrototype("MachineFrame");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue