Compare commits
1 Commits
ed-09-05-2
...
ed_worldev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8ce6d0141 |
@@ -12,7 +12,6 @@ tab_width = 4
|
||||
#end_of_line = crlf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 120
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
|
||||
@@ -337,11 +336,7 @@ dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter
|
||||
|
||||
# ReSharper properties
|
||||
resharper_braces_for_ifelse = required_for_multiline
|
||||
resharper_csharp_wrap_arguments_style = chop_if_long
|
||||
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
|
||||
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
||||
indent_size = 2
|
||||
|
||||
16
.github/labeler.yml
vendored
16
.github/labeler.yml
vendored
@@ -1,18 +1,12 @@
|
||||
"Changes: Sprites":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.rsi/*.png'
|
||||
- '**/*.rsi/*.png'
|
||||
|
||||
"Changes: Map":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'Resources/Maps/*.yml'
|
||||
- 'Resources/Prototypes/Maps/*.yml'
|
||||
- 'Resources/Maps/*.yml'
|
||||
- 'Resources/Prototypes/Maps/*.yml'
|
||||
|
||||
"Changes: UI":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.xaml*'
|
||||
- '**/*.xaml*'
|
||||
|
||||
"No C#":
|
||||
- changed-files:
|
||||
# Equiv to any-glob-to-all as long as this has one matcher. If ALL changed files are not C# files, then apply label.
|
||||
- all-globs-to-all-files: "!**/*.cs"
|
||||
- all: ["!**/*.cs"]
|
||||
|
||||
12
.github/workflows/conflict-labeler.yml
vendored
12
.github/workflows/conflict-labeler.yml
vendored
@@ -1,20 +1,18 @@
|
||||
name: Check Merge Conflicts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
Label:
|
||||
if: ( github.event.pull_request.draft == false ) && ( github.actor != 'PJBot' )
|
||||
if: github.actor != 'PJBot'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for Merge Conflicts
|
||||
uses: eps1lon/actions-label-merge-conflict@v3.0.0
|
||||
uses: ike709/actions-label-merge-conflict@9eefdd17e10566023c46d2dc6dc04fcb8ec76142
|
||||
with:
|
||||
dirtyLabel: "Merge Conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
7
.github/workflows/labeler-pr.yml
vendored
7
.github/workflows/labeler-pr.yml
vendored
@@ -6,9 +6,8 @@ on:
|
||||
jobs:
|
||||
labeler:
|
||||
if: github.actor != 'PJBot'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/labeler@v3
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -46,7 +46,7 @@ public class MapLoadBenchmark
|
||||
PoolManager.Shutdown();
|
||||
}
|
||||
|
||||
public static readonly string[] MapsSource = { "Empty", "Box", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry", "Oasis" };
|
||||
public static readonly string[] MapsSource = { "Empty", "Box", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry" };
|
||||
|
||||
[ParamsSource(nameof(MapsSource))]
|
||||
public string Map;
|
||||
|
||||
@@ -58,7 +58,7 @@ public class SpawnEquipDeleteBenchmark
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
_entity = server.EntMan.SpawnAttachedTo(Mob, _coords);
|
||||
_spawnSys.EquipStartingGear(_entity, _gear);
|
||||
_spawnSys.EquipStartingGear(_entity, _gear, null);
|
||||
server.EntMan.DeleteEntity(_entity);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,20 +9,20 @@ namespace Content.Client.Access;
|
||||
|
||||
public sealed class AccessOverlay : Overlay
|
||||
{
|
||||
private const string TextFontPath = "/Fonts/NotoSans/NotoSans-Regular.ttf";
|
||||
private const int TextFontSize = 12;
|
||||
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SharedTransformSystem _transformSystem;
|
||||
private readonly EntityLookupSystem _lookup;
|
||||
private readonly SharedTransformSystem _xform;
|
||||
private readonly Font _font;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
||||
public AccessOverlay(IEntityManager entityManager, IResourceCache resourceCache, SharedTransformSystem transformSystem)
|
||||
public AccessOverlay(IEntityManager entManager, IResourceCache cache, EntityLookupSystem lookup, SharedTransformSystem xform)
|
||||
{
|
||||
_entityManager = entityManager;
|
||||
_transformSystem = transformSystem;
|
||||
_font = resourceCache.GetFont(TextFontPath, TextFontSize);
|
||||
_entityManager = entManager;
|
||||
_lookup = lookup;
|
||||
_xform = xform;
|
||||
|
||||
_font = cache.GetFont("/Fonts/NotoSans/NotoSans-Regular.ttf", 12);
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
@@ -30,65 +30,52 @@ public sealed class AccessOverlay : Overlay
|
||||
if (args.ViewportControl == null)
|
||||
return;
|
||||
|
||||
var textBuffer = new StringBuilder();
|
||||
var query = _entityManager.EntityQueryEnumerator<AccessReaderComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var accessReader, out var transform))
|
||||
var readerQuery = _entityManager.GetEntityQuery<AccessReaderComponent>();
|
||||
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var ent in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldAABB,
|
||||
LookupFlags.Static | LookupFlags.Approximate))
|
||||
{
|
||||
textBuffer.Clear();
|
||||
|
||||
var entityName = _entityManager.ToPrettyString(uid);
|
||||
textBuffer.AppendLine(entityName.Prototype);
|
||||
textBuffer.Append("UID: ");
|
||||
textBuffer.Append(entityName.Uid.Id);
|
||||
textBuffer.Append(", NUID: ");
|
||||
textBuffer.Append(entityName.Nuid.Id);
|
||||
textBuffer.AppendLine();
|
||||
|
||||
if (!accessReader.Enabled)
|
||||
if (!readerQuery.TryGetComponent(ent, out var reader) ||
|
||||
!xformQuery.TryGetComponent(ent, out var xform))
|
||||
{
|
||||
textBuffer.AppendLine("-Disabled");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (accessReader.AccessLists.Count > 0)
|
||||
var text = new StringBuilder();
|
||||
var index = 0;
|
||||
var a = $"{_entityManager.ToPrettyString(ent)}";
|
||||
text.Append(a);
|
||||
|
||||
foreach (var list in reader.AccessLists)
|
||||
{
|
||||
var groupNumber = 0;
|
||||
foreach (var accessList in accessReader.AccessLists)
|
||||
a = $"Tag {index}";
|
||||
text.AppendLine(a);
|
||||
|
||||
foreach (var entry in list)
|
||||
{
|
||||
groupNumber++;
|
||||
foreach (var entry in accessList)
|
||||
{
|
||||
textBuffer.Append("+Set ");
|
||||
textBuffer.Append(groupNumber);
|
||||
textBuffer.Append(": ");
|
||||
textBuffer.Append(entry.Id);
|
||||
textBuffer.AppendLine();
|
||||
}
|
||||
a = $"- {entry}";
|
||||
text.AppendLine(a);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
string textStr;
|
||||
|
||||
if (text.Length >= 2)
|
||||
{
|
||||
textStr = text.ToString();
|
||||
textStr = textStr[..^2];
|
||||
}
|
||||
else
|
||||
{
|
||||
textBuffer.AppendLine("+Unrestricted");
|
||||
textStr = "";
|
||||
}
|
||||
|
||||
foreach (var key in accessReader.AccessKeys)
|
||||
{
|
||||
textBuffer.Append("+Key ");
|
||||
textBuffer.Append(key.OriginStation);
|
||||
textBuffer.Append(": ");
|
||||
textBuffer.Append(key.Id);
|
||||
textBuffer.AppendLine();
|
||||
}
|
||||
var screenPos = args.ViewportControl.WorldToScreen(_xform.GetWorldPosition(xform));
|
||||
|
||||
foreach (var tag in accessReader.DenyTags)
|
||||
{
|
||||
textBuffer.Append("-Tag ");
|
||||
textBuffer.AppendLine(tag.Id);
|
||||
}
|
||||
|
||||
var accessInfoText = textBuffer.ToString();
|
||||
var screenPos = args.ViewportControl.WorldToScreen(_transformSystem.GetWorldPosition(transform));
|
||||
args.ScreenHandle.DrawString(_font, screenPos, accessInfoText, Color.Gold);
|
||||
args.ScreenHandle.DrawString(_font, screenPos, textStr, Color.Gold);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,16 +7,8 @@ namespace Content.Client.Access.Commands;
|
||||
public sealed class ShowAccessReadersCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "showaccessreaders";
|
||||
|
||||
public string Description => "Toggles showing access reader permissions on the map";
|
||||
public string Help => """
|
||||
Overlay Info:
|
||||
-Disabled | The access reader is disabled
|
||||
+Unrestricted | The access reader has no restrictions
|
||||
+Set [Index]: [Tag Name]| A tag in an access set (accessor needs all tags in the set to be allowed by the set)
|
||||
+Key [StationUid]: [StationRecordKeyId] | A StationRecordKey that is allowed
|
||||
-Tag [Tag Name] | A tag that is not allowed (takes priority over other allows)
|
||||
""";
|
||||
public string Description => "Shows all access readers in the viewport";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var collection = IoCManager.Instance;
|
||||
@@ -34,9 +26,10 @@ public sealed class ShowAccessReadersCommand : IConsoleCommand
|
||||
|
||||
var entManager = collection.Resolve<IEntityManager>();
|
||||
var cache = collection.Resolve<IResourceCache>();
|
||||
var lookup = entManager.System<EntityLookupSystem>();
|
||||
var xform = entManager.System<SharedTransformSystem>();
|
||||
|
||||
overlay.AddOverlay(new AccessOverlay(entManager, cache, xform));
|
||||
overlay.AddOverlay(new AccessOverlay(entManager, cache, lookup, xform));
|
||||
shell.WriteLine($"Set access reader debug overlay to true");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,6 @@
|
||||
|
||||
namespace Content.Client.Access;
|
||||
|
||||
public sealed class IdCardSystem : SharedIdCardSystem;
|
||||
public sealed class IdCardSystem : SharedIdCardSystem
|
||||
{
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ namespace Content.Client.Access.UI
|
||||
SendMessage(new AgentIDCardJobChangedMessage(newJob));
|
||||
}
|
||||
|
||||
public void OnJobIconChanged(string newJobIconId)
|
||||
public void OnJobIconChanged(string newJobIcon)
|
||||
{
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIcon));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,7 +57,7 @@ namespace Content.Client.Access.UI
|
||||
|
||||
_window.SetCurrentName(cast.CurrentName);
|
||||
_window.SetCurrentJob(cast.CurrentJob);
|
||||
_window.SetAllowedIcons(cast.Icons, cast.CurrentJobIconId);
|
||||
_window.SetAllowedIcons(cast.Icons);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
@@ -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<string> icons)
|
||||
{
|
||||
IconGrid.DisposeAllChildren();
|
||||
|
||||
@@ -79,10 +79,6 @@ namespace Content.Client.Access.UI
|
||||
jobIconButton.AddChild(jobIconTexture);
|
||||
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID);
|
||||
IconGrid.AddChild(jobIconButton);
|
||||
|
||||
if (jobIconId.Equals(currentJobIconId))
|
||||
jobIconButton.Pressed = true;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class HeadstandComponent : SharedHeadstandComponent
|
||||
{
|
||||
|
||||
|
||||
@@ -3,5 +3,6 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class KillSignComponent : SharedKillSignComponent;
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class KillSignComponent : SharedKillSignComponent
|
||||
{ }
|
||||
|
||||
@@ -126,15 +126,12 @@ namespace Content.Client.Administration.Managers
|
||||
|
||||
public AdminData? GetAdminData(EntityUid uid, bool includeDeAdmin = false)
|
||||
{
|
||||
if (uid == _player.LocalEntity && (_adminData?.Active ?? includeDeAdmin))
|
||||
return _adminData;
|
||||
|
||||
return null;
|
||||
return uid == _player.LocalEntity ? _adminData : null;
|
||||
}
|
||||
|
||||
public AdminData? GetAdminData(ICommonSession session, bool includeDeAdmin = false)
|
||||
{
|
||||
if (_player.LocalUser == session.UserId && (_adminData?.Active ?? includeDeAdmin))
|
||||
if (_player.LocalUser == session.UserId)
|
||||
return _adminData;
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -14,12 +11,10 @@ namespace Content.Client.Administration.Systems
|
||||
{
|
||||
[Dependency] private readonly IClientConGroupController _clientConGroupController = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _clientConsoleHost = default!;
|
||||
[Dependency] private readonly ISharedAdminManager _admin = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddAdminVerbs);
|
||||
|
||||
}
|
||||
|
||||
private void AddAdminVerbs(GetVerbsEvent<Verb> args)
|
||||
@@ -38,24 +33,6 @@ namespace Content.Client.Administration.Systems
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
if (!_admin.IsAdmin(args.User))
|
||||
return;
|
||||
|
||||
if (_admin.HasAdminFlag(args.User, AdminFlags.Admin))
|
||||
args.ExtraCategories.Add(VerbCategory.Admin);
|
||||
|
||||
if (_admin.HasAdminFlag(args.User, AdminFlags.Fun) && HasComp<MindContainerComponent>(args.Target))
|
||||
args.ExtraCategories.Add(VerbCategory.Antag);
|
||||
|
||||
if (_admin.HasAdminFlag(args.User, AdminFlags.Debug))
|
||||
args.ExtraCategories.Add(VerbCategory.Debug);
|
||||
|
||||
if (_admin.HasAdminFlag(args.User, AdminFlags.Fun))
|
||||
args.ExtraCategories.Add(VerbCategory.Smite);
|
||||
|
||||
if (_admin.HasAdminFlag(args.User, AdminFlags.Admin))
|
||||
args.ExtraCategories.Add(VerbCategory.Tricks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Content.Client.Administration.UI.CustomControls;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.AutoGenerated;
|
||||
@@ -12,7 +11,6 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -34,11 +32,8 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
// This is less efficient than just holding a reference to the root control and enumerating children, but you
|
||||
// have to know how the controls are nested, which makes the code more complicated.
|
||||
private readonly List<CheckBox> _roleCheckboxes = new();
|
||||
private readonly ISawmill _banpanelSawmill;
|
||||
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private enum TabNumbers
|
||||
{
|
||||
@@ -70,7 +65,6 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_banpanelSawmill = _logManager.GetSawmill("admin.banpanel");
|
||||
PlayerList.OnSelectionChanged += OnPlayerSelectionChanged;
|
||||
PlayerNameLine.OnFocusExit += _ => OnPlayerNameChanged();
|
||||
PlayerCheckbox.OnPressed += _ =>
|
||||
@@ -110,11 +104,6 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
};
|
||||
SubmitButton.OnPressed += SubmitButtonOnOnPressed;
|
||||
|
||||
IpCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanIpBanDefault);
|
||||
HwidCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanHwidBanDefault);
|
||||
LastConnCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanUseLastDetails);
|
||||
EraseCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanErasePlayer);
|
||||
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-none"), (int) NoteSeverity.None);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) NoteSeverity.Minor);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) NoteSeverity.Medium);
|
||||
@@ -186,39 +175,6 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
c.Pressed = args.Pressed;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.Pressed)
|
||||
{
|
||||
if (!Enum.TryParse(_cfg.GetCVar(CCVars.DepartmentBanDefaultSeverity), true, out NoteSeverity newSeverity))
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Departmental role ban severity could not be parsed from config!");
|
||||
return;
|
||||
}
|
||||
SeverityOption.SelectId((int) newSeverity);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var childContainer in RolesContainer.Children)
|
||||
{
|
||||
if (childContainer is Container)
|
||||
{
|
||||
foreach (var child in childContainer.Children)
|
||||
{
|
||||
if (child is CheckBox { Pressed: true })
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), true, out NoteSeverity newSeverity))
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Role ban severity could not be parsed from config!");
|
||||
return;
|
||||
}
|
||||
SeverityOption.SelectId((int) newSeverity);
|
||||
}
|
||||
};
|
||||
outerContainer.AddChild(innerContainer);
|
||||
foreach (var role in roleList)
|
||||
@@ -397,35 +353,6 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
TypeOption.ModulateSelfOverride = null;
|
||||
Tabs.SetTabVisible((int) TabNumbers.Roles, TypeOption.SelectedId == (int) Types.Role);
|
||||
NoteSeverity? newSeverity = null;
|
||||
switch (TypeOption.SelectedId)
|
||||
{
|
||||
case (int)Types.Server:
|
||||
if (Enum.TryParse(_cfg.GetCVar(CCVars.ServerBanDefaultSeverity), true, out NoteSeverity serverSeverity))
|
||||
newSeverity = serverSeverity;
|
||||
else
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Server ban severity could not be parsed from config!");
|
||||
}
|
||||
|
||||
break;
|
||||
case (int) Types.Role:
|
||||
|
||||
if (Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), true, out NoteSeverity roleSeverity))
|
||||
{
|
||||
newSeverity = roleSeverity;
|
||||
}
|
||||
else
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Role ban severity could not be parsed from config!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (newSeverity != null)
|
||||
SeverityOption.SelectId((int) newSeverity.Value);
|
||||
}
|
||||
|
||||
private void UpdateSubmitEnabled()
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Content.Client.Animations;
|
||||
|
||||
/// <summary>
|
||||
/// Entities with this component tracks the user's world position every frame.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TrackUserComponent : Component
|
||||
{
|
||||
public EntityUid? User;
|
||||
|
||||
/// <summary>
|
||||
/// Offset in the direction of the entity's rotation.
|
||||
/// </summary>
|
||||
public Vector2 Offset = Vector2.Zero;
|
||||
}
|
||||
@@ -163,26 +163,6 @@ namespace Content.Client.Atmos.UI
|
||||
parent.AddChild(panel);
|
||||
panel.AddChild(dataContainer);
|
||||
|
||||
// Volume label
|
||||
var volBox = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal };
|
||||
|
||||
volBox.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("gas-analyzer-window-volume-text")
|
||||
});
|
||||
volBox.AddChild(new Control
|
||||
{
|
||||
MinSize = new Vector2(10, 0),
|
||||
HorizontalExpand = true
|
||||
});
|
||||
volBox.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("gas-analyzer-window-volume-val-text", ("volume", $"{gasMix.Volume:0.##}")),
|
||||
Align = Label.AlignMode.Right,
|
||||
HorizontalExpand = true
|
||||
});
|
||||
dataContainer.AddChild(volBox);
|
||||
|
||||
// Pressure label
|
||||
var presBox = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal };
|
||||
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
using Content.Shared.Audio.Jukebox;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Audio.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Audio.Jukebox;
|
||||
|
||||
public sealed class JukeboxBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private JukeboxMenu? _menu;
|
||||
|
||||
public JukeboxBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = new JukeboxMenu();
|
||||
_menu.OnClose += Close;
|
||||
_menu.OpenCentered();
|
||||
|
||||
_menu.OnPlayPressed += args =>
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
SendMessage(new JukeboxPlayingMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessage(new JukeboxPauseMessage());
|
||||
}
|
||||
};
|
||||
|
||||
_menu.OnStopPressed += () =>
|
||||
{
|
||||
SendMessage(new JukeboxStopMessage());
|
||||
};
|
||||
|
||||
_menu.OnSongSelected += SelectSong;
|
||||
|
||||
_menu.SetTime += SetTime;
|
||||
PopulateMusic();
|
||||
Reload();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the attached menu if it exists.
|
||||
/// </summary>
|
||||
public void Reload()
|
||||
{
|
||||
if (_menu == null || !EntMan.TryGetComponent(Owner, out JukeboxComponent? jukebox))
|
||||
return;
|
||||
|
||||
_menu.SetAudioStream(jukebox.AudioStream);
|
||||
|
||||
if (_protoManager.TryIndex(jukebox.SelectedSongId, out var songProto))
|
||||
{
|
||||
var length = EntMan.System<AudioSystem>().GetAudioLength(songProto.Path.Path.ToString());
|
||||
_menu.SetSelectedSong(songProto.Name, (float) length.TotalSeconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
_menu.SetSelectedSong(string.Empty, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
public void PopulateMusic()
|
||||
{
|
||||
_menu?.Populate(_protoManager.EnumeratePrototypes<JukeboxPrototype>());
|
||||
}
|
||||
|
||||
public void SelectSong(ProtoId<JukeboxPrototype> songid)
|
||||
{
|
||||
SendMessage(new JukeboxSelectedMessage(songid));
|
||||
}
|
||||
|
||||
public void SetTime(float time)
|
||||
{
|
||||
var sentTime = time;
|
||||
|
||||
// You may be wondering, what the fuck is this
|
||||
// Well we want to be able to predict the playback slider change, of which there are many ways to do it
|
||||
// We can't just use SendPredictedMessage because it will reset every tick and audio updates every frame
|
||||
// so it will go BRRRRT
|
||||
// Using ping gets us close enough that it SHOULD, MOST OF THE TIME, fall within the 0.1 second tolerance
|
||||
// that's still on engine so our playback position never gets corrected.
|
||||
if (EntMan.TryGetComponent(Owner, out JukeboxComponent? jukebox) &&
|
||||
EntMan.TryGetComponent(jukebox.AudioStream, out AudioComponent? audioComp))
|
||||
{
|
||||
audioComp.PlaybackPosition = time;
|
||||
}
|
||||
|
||||
SendMessage(new JukeboxSetTimeMessage(sentTime));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_menu == null)
|
||||
return;
|
||||
|
||||
_menu.OnClose -= Close;
|
||||
_menu.Dispose();
|
||||
_menu = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<ui:FancyWindow xmlns="https://spacestation14.io" xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
SetSize="400 500" Title="{Loc 'jukebox-menu-title'}">
|
||||
<BoxContainer Margin="4 0" Orientation="Vertical">
|
||||
<ItemList Name="MusicList" SelectMode="Button" Margin="3 3 3 3"
|
||||
HorizontalExpand="True" VerticalExpand="True" SizeFlagsStretchRatio="8"/>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Name="SongSelected" Text="{Loc 'jukebox-menu-selectedsong'}" />
|
||||
<Label Name="SongName" Text="---" />
|
||||
<Slider Name="PlaybackSlider" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"
|
||||
VerticalExpand="False" SizeFlagsStretchRatio="1">
|
||||
<Button Name="PlayButton" Text="{Loc 'jukebox-menu-buttonplay'}" />
|
||||
<Button Name="StopButton" Text="{Loc 'jukebox-menu-buttonstop'}" />
|
||||
<Label Name="DurationLabel" Text="00:00 / 00:00" HorizontalAlignment="Right" HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ui:FancyWindow>
|
||||
@@ -1,166 +0,0 @@
|
||||
using Content.Shared.Audio.Jukebox;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Audio.Components;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using FancyWindow = Content.Client.UserInterface.Controls.FancyWindow;
|
||||
|
||||
namespace Content.Client.Audio.Jukebox;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class JukeboxMenu : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
private AudioSystem _audioSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Are we currently 'playing' or paused for the play / pause button.
|
||||
/// </summary>
|
||||
private bool _playState;
|
||||
|
||||
/// <summary>
|
||||
/// True if playing, false if paused.
|
||||
/// </summary>
|
||||
public event Action<bool>? OnPlayPressed;
|
||||
public event Action? OnStopPressed;
|
||||
public event Action<ProtoId<JukeboxPrototype>>? OnSongSelected;
|
||||
public event Action<float>? SetTime;
|
||||
|
||||
private EntityUid? _audio;
|
||||
|
||||
private float _lockTimer;
|
||||
|
||||
public JukeboxMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_audioSystem = _entManager.System<AudioSystem>();
|
||||
|
||||
MusicList.OnItemSelected += args =>
|
||||
{
|
||||
var entry = MusicList[args.ItemIndex];
|
||||
|
||||
if (entry.Metadata is not string juke)
|
||||
return;
|
||||
|
||||
OnSongSelected?.Invoke(juke);
|
||||
};
|
||||
|
||||
PlayButton.OnPressed += args =>
|
||||
{
|
||||
OnPlayPressed?.Invoke(!_playState);
|
||||
};
|
||||
|
||||
StopButton.OnPressed += args =>
|
||||
{
|
||||
OnStopPressed?.Invoke();
|
||||
};
|
||||
PlaybackSlider.OnReleased += PlaybackSliderKeyUp;
|
||||
|
||||
SetPlayPauseButton(_audioSystem.IsPlaying(_audio), force: true);
|
||||
}
|
||||
|
||||
public JukeboxMenu(AudioSystem audioSystem)
|
||||
{
|
||||
_audioSystem = audioSystem;
|
||||
}
|
||||
|
||||
public void SetAudioStream(EntityUid? audio)
|
||||
{
|
||||
_audio = audio;
|
||||
}
|
||||
|
||||
private void PlaybackSliderKeyUp(Slider args)
|
||||
{
|
||||
SetTime?.Invoke(PlaybackSlider.Value);
|
||||
_lockTimer = 0.5f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-populates the list of jukebox prototypes available.
|
||||
/// </summary>
|
||||
public void Populate(IEnumerable<JukeboxPrototype> jukeboxProtos)
|
||||
{
|
||||
MusicList.Clear();
|
||||
|
||||
foreach (var entry in jukeboxProtos)
|
||||
{
|
||||
MusicList.AddItem(entry.Name, metadata: entry.ID);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPlayPauseButton(bool playing, bool force = false)
|
||||
{
|
||||
if (_playState == playing && !force)
|
||||
return;
|
||||
|
||||
_playState = playing;
|
||||
|
||||
if (playing)
|
||||
{
|
||||
PlayButton.Text = Loc.GetString("jukebox-menu-buttonpause");
|
||||
return;
|
||||
}
|
||||
|
||||
PlayButton.Text = Loc.GetString("jukebox-menu-buttonplay");
|
||||
}
|
||||
|
||||
public void SetSelectedSong(string name, float length)
|
||||
{
|
||||
SetSelectedSongText(name);
|
||||
PlaybackSlider.MaxValue = length;
|
||||
PlaybackSlider.SetValueWithoutEvent(0);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_lockTimer > 0f)
|
||||
{
|
||||
_lockTimer -= args.DeltaSeconds;
|
||||
}
|
||||
|
||||
PlaybackSlider.Disabled = _lockTimer > 0f;
|
||||
|
||||
if (_entManager.TryGetComponent(_audio, out AudioComponent? audio))
|
||||
{
|
||||
DurationLabel.Text = $@"{TimeSpan.FromSeconds(audio.PlaybackPosition):mm\:ss} / {_audioSystem.GetAudioLength(audio.FileName):mm\:ss}";
|
||||
}
|
||||
else
|
||||
{
|
||||
DurationLabel.Text = $"00:00 / 00:00";
|
||||
}
|
||||
|
||||
if (PlaybackSlider.Grabbed)
|
||||
return;
|
||||
|
||||
if (audio != null || _entManager.TryGetComponent(_audio, out audio))
|
||||
{
|
||||
PlaybackSlider.SetValueWithoutEvent(audio.PlaybackPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaybackSlider.SetValueWithoutEvent(0f);
|
||||
}
|
||||
|
||||
SetPlayPauseButton(_audioSystem.IsPlaying(_audio, audio));
|
||||
}
|
||||
|
||||
public void SetSelectedSongText(string? text)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
SongName.Text = text;
|
||||
}
|
||||
else
|
||||
{
|
||||
SongName.Text = "---";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
using Content.Shared.Audio.Jukebox;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Audio.Jukebox;
|
||||
|
||||
|
||||
public sealed class JukeboxSystem : SharedJukeboxSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<JukeboxComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
SubscribeLocalEvent<JukeboxComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
SubscribeLocalEvent<JukeboxComponent, AfterAutoHandleStateEvent>(OnJukeboxAfterState);
|
||||
|
||||
_protoManager.PrototypesReloaded += OnProtoReload;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_protoManager.PrototypesReloaded -= OnProtoReload;
|
||||
}
|
||||
|
||||
private void OnProtoReload(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
if (!obj.WasModified<JukeboxPrototype>())
|
||||
return;
|
||||
|
||||
var query = AllEntityQuery<JukeboxComponent, UserInterfaceComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _, out var ui))
|
||||
{
|
||||
if (!_uiSystem.TryGetOpenUi<JukeboxBoundUserInterface>((uid, ui), JukeboxUiKey.Key, out var bui))
|
||||
continue;
|
||||
|
||||
bui.PopulateMusic();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnJukeboxAfterState(Entity<JukeboxComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (!_uiSystem.TryGetOpenUi<JukeboxBoundUserInterface>(ent.Owner, JukeboxUiKey.Key, out var bui))
|
||||
return;
|
||||
|
||||
bui.Reload();
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(EntityUid uid, JukeboxComponent component, AnimationCompletedEvent args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
if (!TryComp<AppearanceComponent>(uid, out var appearance) ||
|
||||
!_appearanceSystem.TryGetData<JukeboxVisualState>(uid, JukeboxVisuals.VisualState, out var visualState, appearance))
|
||||
{
|
||||
visualState = JukeboxVisualState.On;
|
||||
}
|
||||
|
||||
UpdateAppearance(uid, visualState, component, sprite);
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, JukeboxComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (!args.AppearanceData.TryGetValue(JukeboxVisuals.VisualState, out var visualStateObject) ||
|
||||
visualStateObject is not JukeboxVisualState visualState)
|
||||
{
|
||||
visualState = JukeboxVisualState.On;
|
||||
}
|
||||
|
||||
UpdateAppearance(uid, visualState, component, args.Sprite);
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, JukeboxVisualState visualState, JukeboxComponent component, SpriteComponent sprite)
|
||||
{
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OffState, sprite);
|
||||
|
||||
switch (visualState)
|
||||
{
|
||||
case JukeboxVisualState.On:
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OnState, sprite);
|
||||
break;
|
||||
|
||||
case JukeboxVisualState.Off:
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OffState, sprite);
|
||||
break;
|
||||
|
||||
case JukeboxVisualState.Select:
|
||||
PlayAnimation(uid, JukeboxVisualLayers.Base, component.SelectState, 1.0f, sprite);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayAnimation(EntityUid uid, JukeboxVisualLayers layer, string? state, float animationTime, SpriteComponent sprite)
|
||||
{
|
||||
if (string.IsNullOrEmpty(state))
|
||||
return;
|
||||
|
||||
if (!_animationPlayer.HasRunningAnimation(uid, state))
|
||||
{
|
||||
var animation = GetAnimation(layer, state, animationTime);
|
||||
sprite.LayerSetVisible(layer, true);
|
||||
_animationPlayer.Play(uid, animation, state);
|
||||
}
|
||||
}
|
||||
|
||||
private static Animation GetAnimation(JukeboxVisualLayers layer, string state, float animationTime)
|
||||
{
|
||||
return new Animation
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(animationTime),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick
|
||||
{
|
||||
LayerKey = layer,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackSpriteFlick.KeyFrame(state, 0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void SetLayerState(JukeboxVisualLayers layer, string? state, SpriteComponent sprite)
|
||||
{
|
||||
if (string.IsNullOrEmpty(state))
|
||||
return;
|
||||
|
||||
sprite.LayerSetVisible(layer, true);
|
||||
sprite.LayerSetAutoAnimated(layer, true);
|
||||
sprite.LayerSetState(layer, state);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.CardboardBox;
|
||||
using Content.Shared.CardboardBox.Components;
|
||||
using Content.Shared.Examine;
|
||||
@@ -14,14 +13,9 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
||||
|
||||
private EntityQuery<BodyComponent> _bodyQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_bodyQuery = GetEntityQuery<BodyComponent>();
|
||||
|
||||
SubscribeNetworkEvent<PlayBoxEffectMessage>(OnBoxEffect);
|
||||
}
|
||||
|
||||
@@ -65,10 +59,6 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
|
||||
if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null))
|
||||
continue;
|
||||
|
||||
// no effect for anything too exotic
|
||||
if (!_bodyQuery.HasComp(mob))
|
||||
continue;
|
||||
|
||||
var ent = Spawn(box.Effect, mapPos);
|
||||
|
||||
if (!xformQuery.TryGetComponent(ent, out var entTransform) || !TryComp<SpriteComponent>(ent, out var sprite))
|
||||
|
||||
@@ -27,11 +27,6 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
|
||||
SendMessage(new BountyPrintLabelMessage(id));
|
||||
};
|
||||
|
||||
_menu.OnSkipButtonPressed += id =>
|
||||
{
|
||||
SendMessage(new BountySkipMessage(id));
|
||||
};
|
||||
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
@@ -42,7 +37,7 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
|
||||
if (message is not CargoBountyConsoleState state)
|
||||
return;
|
||||
|
||||
_menu?.UpdateEntries(state.Bounties, state.UntilNextSkip);
|
||||
_menu?.UpdateEntries(state.Bounties);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
@@ -13,18 +13,7 @@
|
||||
</BoxContainer>
|
||||
<Control MinWidth="10"/>
|
||||
<BoxContainer Orientation="Vertical" MinWidth="120">
|
||||
<BoxContainer Orientation="Horizontal" MinWidth="120">
|
||||
<Button Name="PrintButton"
|
||||
Text="{Loc 'bounty-console-label-button-text'}"
|
||||
HorizontalExpand="False"
|
||||
HorizontalAlignment="Right"
|
||||
StyleClasses="OpenRight"/>
|
||||
<Button Name="SkipButton"
|
||||
Text="{Loc 'bounty-console-skip-button-text'}"
|
||||
HorizontalExpand="False"
|
||||
HorizontalAlignment="Right"
|
||||
StyleClasses="OpenLeft"/>
|
||||
</BoxContainer>
|
||||
<Button Name="PrintButton" Text="{Loc 'bounty-console-label-button-text'}" HorizontalExpand="False" HorizontalAlignment="Right"/>
|
||||
<RichTextLabel Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Shared.Cargo;
|
||||
using Content.Shared.Cargo.Prototypes;
|
||||
using Content.Shared.Random;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Serilog;
|
||||
|
||||
namespace Content.Client.Cargo.UI;
|
||||
|
||||
@@ -16,19 +14,15 @@ public sealed partial class BountyEntry : BoxContainer
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public Action? OnLabelButtonPressed;
|
||||
public Action? OnSkipButtonPressed;
|
||||
public Action? OnButtonPressed;
|
||||
|
||||
public TimeSpan EndTime;
|
||||
public TimeSpan UntilNextSkip;
|
||||
|
||||
public BountyEntry(CargoBountyData bounty, TimeSpan untilNextSkip)
|
||||
public BountyEntry(CargoBountyData bounty)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
UntilNextSkip = untilNextSkip;
|
||||
|
||||
if (!_prototype.TryIndex<CargoBountyPrototype>(bounty.Bounty, out var bountyPrototype))
|
||||
return;
|
||||
|
||||
@@ -44,27 +38,6 @@ public sealed partial class BountyEntry : BoxContainer
|
||||
DescriptionLabel.SetMarkup(Loc.GetString("bounty-console-description-label", ("description", Loc.GetString(bountyPrototype.Description))));
|
||||
IdLabel.SetMarkup(Loc.GetString("bounty-console-id-label", ("id", bounty.Id)));
|
||||
|
||||
PrintButton.OnPressed += _ => OnLabelButtonPressed?.Invoke();
|
||||
SkipButton.OnPressed += _ => OnSkipButtonPressed?.Invoke();
|
||||
}
|
||||
|
||||
private void UpdateSkipButton(float deltaSeconds)
|
||||
{
|
||||
UntilNextSkip -= TimeSpan.FromSeconds(deltaSeconds);
|
||||
if (UntilNextSkip > TimeSpan.Zero)
|
||||
{
|
||||
SkipButton.Label.Text = UntilNextSkip.ToString("mm\\:ss");
|
||||
SkipButton.Disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
SkipButton.Label.Text = Loc.GetString("bounty-console-skip-button-text");
|
||||
SkipButton.Disabled = false;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
UpdateSkipButton(args.DeltaSeconds);
|
||||
PrintButton.OnPressed += _ => OnButtonPressed?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,21 +10,19 @@ namespace Content.Client.Cargo.UI;
|
||||
public sealed partial class CargoBountyMenu : FancyWindow
|
||||
{
|
||||
public Action<string>? OnLabelButtonPressed;
|
||||
public Action<string>? OnSkipButtonPressed;
|
||||
|
||||
public CargoBountyMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void UpdateEntries(List<CargoBountyData> bounties, TimeSpan untilNextSkip)
|
||||
public void UpdateEntries(List<CargoBountyData> bounties)
|
||||
{
|
||||
BountyEntriesContainer.Children.Clear();
|
||||
foreach (var b in bounties)
|
||||
{
|
||||
var entry = new BountyEntry(b, untilNextSkip);
|
||||
entry.OnLabelButtonPressed += () => OnLabelButtonPressed?.Invoke(b.Id);
|
||||
entry.OnSkipButtonPressed += () => OnSkipButtonPressed?.Invoke(b.Id);
|
||||
var entry = new BountyEntry(b);
|
||||
entry.OnButtonPressed += () => OnLabelButtonPressed?.Invoke(b.Id);
|
||||
|
||||
BountyEntriesContainer.AddChild(entry);
|
||||
}
|
||||
|
||||
@@ -87,12 +87,14 @@ namespace Content.Client.Changelog
|
||||
if (!tab.AdminOnly || isAdmin)
|
||||
{
|
||||
Tabs.SetTabVisible(i, true);
|
||||
tab.Visible = true;
|
||||
visibleTabs++;
|
||||
firstVisible ??= i;
|
||||
}
|
||||
else
|
||||
{
|
||||
Tabs.SetTabVisible(i, false);
|
||||
tab.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<ui:RadialMenu xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
BackButtonStyleClass="RadialMenuBackButton"
|
||||
CloseButtonStyleClass="RadialMenuCloseButton"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
MinSize="450 450">
|
||||
|
||||
<!-- Main -->
|
||||
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" Radius="64" ReserveSpaceForHiddenChildren="False">
|
||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-general'}" TargetLayer="General" Visible="False">
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png"/>
|
||||
</ui:RadialMenuTextureButton>
|
||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-vocal'}" TargetLayer="Vocal" Visible="False">
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Emotes/vocal.png"/>
|
||||
</ui:RadialMenuTextureButton>
|
||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-hands'}" TargetLayer="Hands" Visible="False">
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png"/>
|
||||
</ui:RadialMenuTextureButton>
|
||||
</ui:RadialContainer>
|
||||
|
||||
<!-- General -->
|
||||
<ui:RadialContainer Name="General" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
||||
|
||||
<!-- Vocal -->
|
||||
<ui:RadialContainer Name="Vocal" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
||||
|
||||
<!-- Hands -->
|
||||
<ui:RadialContainer Name="Hands" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
||||
|
||||
</ui:RadialMenu>
|
||||
@@ -1,112 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Speech;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Chat.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class EmotesMenu : RadialMenu
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
|
||||
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
|
||||
public event Action<ProtoId<EmotePrototype>>? OnPlayEmote;
|
||||
|
||||
public EmotesMenu()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||
|
||||
var main = FindControl<RadialContainer>("Main");
|
||||
|
||||
var emotes = _prototypeManager.EnumeratePrototypes<EmotePrototype>();
|
||||
foreach (var emote in emotes)
|
||||
{
|
||||
var player = _playerManager.LocalSession?.AttachedEntity;
|
||||
if (emote.Category == EmoteCategory.Invalid ||
|
||||
emote.ChatTriggers.Count == 0 ||
|
||||
!(player.HasValue && (emote.Whitelist?.IsValid(player.Value, _entManager) ?? true)) ||
|
||||
(emote.Blacklist?.IsValid(player.Value, _entManager) ?? false))
|
||||
continue;
|
||||
|
||||
if (!emote.Available &&
|
||||
_entManager.TryGetComponent<SpeechComponent>(player.Value, out var speech) &&
|
||||
!speech.AllowedEmotes.Contains(emote.ID))
|
||||
continue;
|
||||
|
||||
var parent = FindControl<RadialContainer>(emote.Category.ToString());
|
||||
|
||||
var button = new EmoteMenuButton
|
||||
{
|
||||
StyleClasses = { "RadialMenuButton" },
|
||||
SetSize = new Vector2(64f, 64f),
|
||||
ToolTip = Loc.GetString(emote.Name),
|
||||
ProtoId = emote.ID,
|
||||
};
|
||||
|
||||
var tex = new TextureRect
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Texture = _spriteSystem.Frame0(emote.Icon),
|
||||
TextureScale = new Vector2(2f, 2f),
|
||||
};
|
||||
|
||||
button.AddChild(tex);
|
||||
parent.AddChild(button);
|
||||
foreach (var child in main.Children)
|
||||
{
|
||||
if (child is not RadialMenuTextureButton castChild)
|
||||
continue;
|
||||
|
||||
if (castChild.TargetLayer == emote.Category.ToString())
|
||||
{
|
||||
castChild.Visible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set up menu actions
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is not RadialContainer container)
|
||||
continue;
|
||||
AddEmoteClickAction(container);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddEmoteClickAction(RadialContainer container)
|
||||
{
|
||||
foreach (var child in container.Children)
|
||||
{
|
||||
if (child is not EmoteMenuButton castChild)
|
||||
continue;
|
||||
|
||||
castChild.OnButtonUp += _ =>
|
||||
{
|
||||
OnPlayEmote?.Invoke(castChild.ProtoId);
|
||||
Close();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public sealed class EmoteMenuButton : RadialMenuTextureButton
|
||||
{
|
||||
public ProtoId<EmotePrototype> ProtoId { get; set; }
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using Content.Client.Chemistry.EntitySystems;
|
||||
using Content.Client.Chemistry.UI;
|
||||
|
||||
namespace Content.Client.Chemistry.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Exposes a solution container's contents via a basic item status control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Shows the solution volume, max volume, and transfer amount.
|
||||
/// </remarks>
|
||||
/// <seealso cref="SolutionItemStatusSystem"/>
|
||||
/// <seealso cref="SolutionStatusControl"/>
|
||||
[RegisterComponent]
|
||||
public sealed partial class SolutionItemStatusComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the solution that will be shown on the item status control.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Solution = "default";
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using Content.Client.Chemistry.Components;
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.Client.Items;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Client.Chemistry.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Wires up item status logic for <see cref="SolutionItemStatusComponent"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="SolutionStatusControl"/>
|
||||
public sealed class SolutionItemStatusSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Subs.ItemStatus<SolutionItemStatusComponent>(
|
||||
entity => new SolutionStatusControl(entity, EntityManager, _solutionContainerSystem));
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public sealed class InjectorStatusControl : Control
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, InjectorComponent.SolutionName, out _, out var solution))
|
||||
return;
|
||||
|
||||
// only updates the UI if any of the details are different than they previously were
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
using Content.Client.Chemistry.Components;
|
||||
using Content.Client.Chemistry.EntitySystems;
|
||||
using Content.Client.Items.UI;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Chemistry.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Displays basic solution information for <see cref="SolutionItemStatusComponent"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="SolutionItemStatusSystem"/>
|
||||
public sealed class SolutionStatusControl : PollingItemStatusControl<SolutionStatusControl.Data>
|
||||
{
|
||||
private readonly Entity<SolutionItemStatusComponent> _parent;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SharedSolutionContainerSystem _solutionContainers;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
public SolutionStatusControl(
|
||||
Entity<SolutionItemStatusComponent> parent,
|
||||
IEntityManager entityManager,
|
||||
SharedSolutionContainerSystem solutionContainers)
|
||||
{
|
||||
_parent = parent;
|
||||
_entityManager = entityManager;
|
||||
_solutionContainers = solutionContainers;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
AddChild(_label);
|
||||
}
|
||||
|
||||
protected override Data PollData()
|
||||
{
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.Solution, out _, out var solution))
|
||||
return default;
|
||||
|
||||
FixedPoint2? transferAmount = null;
|
||||
if (_entityManager.TryGetComponent(_parent.Owner, out SolutionTransferComponent? transfer))
|
||||
transferAmount = transfer.TransferAmount;
|
||||
|
||||
return new Data(solution.Volume, solution.MaxVolume, transferAmount);
|
||||
}
|
||||
|
||||
protected override void Update(in Data data)
|
||||
{
|
||||
var markup = Loc.GetString("solution-status-volume",
|
||||
("currentVolume", data.Volume),
|
||||
("maxVolume", data.MaxVolume));
|
||||
if (data.TransferVolume is { } transferVolume)
|
||||
markup += "\n" + Loc.GetString("solution-status-transfer", ("volume", transferVolume));
|
||||
_label.SetMarkup(markup);
|
||||
}
|
||||
|
||||
public readonly record struct Data(FixedPoint2 Volume, FixedPoint2 MaxVolume, FixedPoint2? TransferVolume);
|
||||
}
|
||||
@@ -11,7 +11,6 @@ using Content.Shared.Item;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.GameObjects.SpriteComponent;
|
||||
@@ -37,24 +36,16 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
{Jumpsuit, "INNERCLOTHING"},
|
||||
{"neck", "NECK"},
|
||||
{"back", "BACKPACK"},
|
||||
{"belt1", "BELT1"},
|
||||
{"belt2", "BELT2"},
|
||||
{"belt", "BELT"},
|
||||
{"gloves", "HAND"},
|
||||
{"shoes", "FEET"},
|
||||
{"id", "IDCARD"},
|
||||
{"pocket1", "POCKET1"},
|
||||
{"pocket2", "POCKET2"},
|
||||
{"suitstorage", "SUITSTORAGE"},
|
||||
{"ring1", "RING1"},
|
||||
{"ring2", "RING2"},
|
||||
{"pants", "PANTS"},
|
||||
{"shirt", "SHIRT"},
|
||||
{"cloak", "CLOAK"},
|
||||
{"keys", "KEYS"},
|
||||
};
|
||||
|
||||
[Dependency] private readonly IResourceCache _cache = default!;
|
||||
[Dependency] private readonly ISerializationManager _serialization = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -274,7 +265,6 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
// temporary, until layer draw depths get added. Basically: a layer with the key "slot" is being used as a
|
||||
// bookmark to determine where in the list of layers we should insert the clothing layers.
|
||||
bool slotLayerExists = sprite.LayerMapTryGet(slot, out var index);
|
||||
var displacementData = inventory.Displacements.GetValueOrDefault(slot);
|
||||
|
||||
// add the new layers
|
||||
foreach (var (key, layerData) in ev.Layers)
|
||||
@@ -318,28 +308,6 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
|
||||
sprite.LayerSetData(index, layerData);
|
||||
layer.Offset += slotDef.Offset;
|
||||
|
||||
if (displacementData != null)
|
||||
{
|
||||
if (displacementData.ShaderOverride != null)
|
||||
sprite.LayerSetShader(index, displacementData.ShaderOverride);
|
||||
|
||||
var displacementKey = $"{key}-displacement";
|
||||
if (!revealedLayers.Add(displacementKey))
|
||||
{
|
||||
Log.Warning($"Duplicate key for clothing visuals DISPLACEMENT: {displacementKey}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var displacementLayer = _serialization.CreateCopy(displacementData.Layer, notNullableOverride: true);
|
||||
displacementLayer.CopyToShaderParameters!.LayerKey = key;
|
||||
|
||||
// Add before main layer for this item.
|
||||
sprite.AddLayer(displacementLayer, index);
|
||||
sprite.LayerMapSet(displacementKey, index);
|
||||
|
||||
revealedLayers.Add(displacementKey);
|
||||
}
|
||||
}
|
||||
|
||||
RaiseLocalEvent(equipment, new EquipmentVisualsUpdatedEvent(equipee, slot, revealedLayers), true);
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Inventory.Events;
|
||||
|
||||
namespace Content.Client.Clothing.Systems;
|
||||
|
||||
public sealed class WaddleClothingSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<WaddleWhenWornComponent, GotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<WaddleWhenWornComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||
}
|
||||
|
||||
private void OnGotEquipped(EntityUid entity, WaddleWhenWornComponent comp, GotEquippedEvent args)
|
||||
{
|
||||
var waddleAnimComp = EnsureComp<WaddleAnimationComponent>(args.Equipee);
|
||||
|
||||
waddleAnimComp.AnimationLength = comp.AnimationLength;
|
||||
waddleAnimComp.HopIntensity = comp.HopIntensity;
|
||||
waddleAnimComp.RunAnimationLengthMultiplier = comp.RunAnimationLengthMultiplier;
|
||||
waddleAnimComp.TumbleIntensity = comp.TumbleIntensity;
|
||||
}
|
||||
|
||||
private void OnGotUnequipped(EntityUid entity, WaddleWhenWornComponent comp, GotUnequippedEvent args)
|
||||
{
|
||||
RemComp<WaddleAnimationComponent>(args.Equipee);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using System.Threading;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Utility;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
@@ -15,8 +13,6 @@ namespace Content.Client.Communications.UI
|
||||
private CommunicationsConsoleBoundUserInterface Owner { get; set; }
|
||||
private readonly CancellationTokenSource _timerCancelTokenSource = new();
|
||||
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -27,22 +23,6 @@ namespace Content.Client.Communications.UI
|
||||
var loc = IoCManager.Resolve<ILocalizationManager>();
|
||||
MessageInput.Placeholder = new Rope.Leaf(loc.GetString("comms-console-menu-announcement-placeholder"));
|
||||
|
||||
var maxAnnounceLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength);
|
||||
MessageInput.OnTextChanged += (args) =>
|
||||
{
|
||||
if (args.Control.TextLength > maxAnnounceLength)
|
||||
{
|
||||
AnnounceButton.Disabled = true;
|
||||
AnnounceButton.ToolTip = Loc.GetString("comms-console-message-too-long");
|
||||
}
|
||||
else
|
||||
{
|
||||
AnnounceButton.Disabled = !owner.CanAnnounce;
|
||||
AnnounceButton.ToolTip = null;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
AnnounceButton.OnPressed += (_) => Owner.AnnounceButtonPressed(Rope.Collapse(MessageInput.TextRope));
|
||||
AnnounceButton.Disabled = !owner.CanAnnounce;
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
using Content.Shared.Radio.EntitySystems;
|
||||
|
||||
namespace Content.Client.DeviceNetwork;
|
||||
|
||||
public sealed class JammerSystem : SharedJammerSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public sealed class DoAfterOverlay : Overlay
|
||||
private readonly ProgressColorSystem _progressColor;
|
||||
|
||||
private readonly Texture _barTexture;
|
||||
private readonly ShaderInstance _unshadedShader;
|
||||
private readonly ShaderInstance _shader;
|
||||
|
||||
/// <summary>
|
||||
/// Flash time for cancelled DoAfters
|
||||
@@ -45,7 +45,7 @@ public sealed class DoAfterOverlay : Overlay
|
||||
var sprite = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/progress_bar.rsi"), "icon");
|
||||
_barTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite);
|
||||
|
||||
_unshadedShader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_shader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
@@ -58,6 +58,7 @@ public sealed class DoAfterOverlay : Overlay
|
||||
const float scale = 1f;
|
||||
var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3.CreateRotation(-rotation);
|
||||
handle.UseShader(_shader);
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
@@ -78,13 +79,6 @@ public sealed class DoAfterOverlay : Overlay
|
||||
if (!bounds.Contains(worldPosition))
|
||||
continue;
|
||||
|
||||
// shades the do-after bar if the do-after bar belongs to other players
|
||||
// does not shade do-afters belonging to the local player
|
||||
if (uid != localEnt)
|
||||
handle.UseShader(null);
|
||||
else
|
||||
handle.UseShader(_unshadedShader);
|
||||
|
||||
// If the entity is paused, we will draw the do-after as it was when the entity got paused.
|
||||
var meta = metaQuery.GetComponent(uid);
|
||||
var time = meta.EntityPaused
|
||||
|
||||
@@ -22,7 +22,6 @@ using Content.Client.Stylesheets;
|
||||
using Content.Client.Viewport;
|
||||
using Content.Client.Voting;
|
||||
using Content.Shared.Ame.Components;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Localizations;
|
||||
using Robust.Client;
|
||||
@@ -119,7 +118,6 @@ namespace Content.Client.Entry
|
||||
_prototypeManager.RegisterIgnore("wireLayout");
|
||||
_prototypeManager.RegisterIgnore("alertLevels");
|
||||
_prototypeManager.RegisterIgnore("nukeopsRole");
|
||||
_prototypeManager.RegisterIgnore("ghostRoleRaffleDecider");
|
||||
|
||||
_componentFactory.GenerateNetIds();
|
||||
_adminManager.Initialize();
|
||||
@@ -159,7 +157,7 @@ namespace Content.Client.Entry
|
||||
_clientPreferencesManager.Initialize();
|
||||
_euiManager.Initialize();
|
||||
_voteManager.Initialize();
|
||||
_userInterfaceManager.SetDefaultTheme(_configManager.GetCVar(CCVars.UIDefaultInterfaceTheme));
|
||||
_userInterfaceManager.SetDefaultTheme("SS14DefaultTheme");
|
||||
_userInterfaceManager.SetActiveTheme(_configManager.GetCVar(CVars.InterfaceTheme));
|
||||
_documentParsingManager.Initialize();
|
||||
|
||||
|
||||
@@ -3,5 +3,7 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Extinguisher;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent;
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent
|
||||
{
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public sealed class EyeLerpingSystem : EntitySystem
|
||||
private void HandleMapChange(EntityUid uid, LerpingEyeComponent component, ref EntParentChangedMessage args)
|
||||
{
|
||||
// Is this actually a map change? If yes, stop any lerps
|
||||
if (args.OldMapId != args.Transform.MapUid)
|
||||
if (args.OldMapId != args.Transform.MapID)
|
||||
component.LastRotation = GetRotation(uid, args.Transform);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Content.Shared.Fax.Components;
|
||||
using Content.Shared.Fax;
|
||||
using Robust.Client.Animations;
|
||||
|
||||
namespace Content.Client.Fax.System;
|
||||
|
||||
/// <summary>
|
||||
/// Visualizer for the fax machine which displays the correct sprite based on the inserted entity.
|
||||
/// </summary>
|
||||
public sealed class FaxVisualsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _player = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<FaxMachineComponent, AppearanceChangeEvent>(OnAppearanceChanged);
|
||||
}
|
||||
|
||||
private void OnAppearanceChanged(EntityUid uid, FaxMachineComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (_appearance.TryGetData(uid, FaxMachineVisuals.VisualState, out FaxMachineVisualState visuals) && visuals == FaxMachineVisualState.Inserting)
|
||||
{
|
||||
_player.Play(uid, new Animation()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(2.4),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = FaxMachineVisuals.VisualState,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackSpriteFlick.KeyFrame(component.InsertingState, 0f),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("icon", 2.4f),
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "faxecute");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public sealed class FaxBoundUi : BoundUserInterface
|
||||
{
|
||||
if (_dialogIsOpen)
|
||||
return;
|
||||
|
||||
|
||||
_dialogIsOpen = true;
|
||||
var filters = new FileDialogFilters(new FileDialogFilters.Group("txt"));
|
||||
await using var file = await _fileDialogManager.OpenFile(filters);
|
||||
@@ -52,27 +52,8 @@ public sealed class FaxBoundUi : BoundUserInterface
|
||||
}
|
||||
|
||||
using var reader = new StreamReader(file);
|
||||
|
||||
var firstLine = await reader.ReadLineAsync();
|
||||
string? label = null;
|
||||
var content = await reader.ReadToEndAsync();
|
||||
|
||||
if (firstLine is { })
|
||||
{
|
||||
if (firstLine.StartsWith('#'))
|
||||
{
|
||||
label = firstLine[1..].Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
content = firstLine + "\n" + content;
|
||||
}
|
||||
}
|
||||
|
||||
SendMessage(new FaxFileMessage(
|
||||
label?[..Math.Min(label.Length, FaxFileMessageValidation.MaxLabelSize)],
|
||||
content[..Math.Min(content.Length, FaxFileMessageValidation.MaxContentSize)],
|
||||
_window.OfficePaper));
|
||||
SendMessage(new FaxFileMessage(content[..Math.Min(content.Length, FaxFileMessageValidation.MaxContentSize)], _window.OfficePaper));
|
||||
}
|
||||
|
||||
private void OnSendButtonPressed()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.RoundEnd;
|
||||
@@ -7,7 +6,7 @@ using Content.Shared.GameWindow;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.GameTicking.Managers
|
||||
{
|
||||
@@ -15,14 +14,17 @@ namespace Content.Client.GameTicking.Managers
|
||||
public sealed class ClientGameTicker : SharedGameTicker
|
||||
{
|
||||
[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!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
[ViewVariables] private bool _initialized;
|
||||
private Dictionary<NetEntity, Dictionary<string, uint?>> _jobsAvailable = new();
|
||||
private Dictionary<NetEntity, string> _stationNames = new();
|
||||
|
||||
/// <summary>
|
||||
/// The current round-end window. Could be used to support re-opening the window after closing it.
|
||||
/// </summary>
|
||||
private RoundEndSummaryWindow? _window;
|
||||
|
||||
[ViewVariables] public bool AreWeReady { get; private set; }
|
||||
[ViewVariables] public bool IsGameStarted { get; private set; }
|
||||
[ViewVariables] public string? RestartSound { get; private set; }
|
||||
@@ -42,6 +44,8 @@ namespace Content.Client.GameTicking.Managers
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
DebugTools.Assert(!_initialized);
|
||||
|
||||
SubscribeNetworkEvent<TickerJoinLobbyEvent>(JoinLobby);
|
||||
SubscribeNetworkEvent<TickerJoinGameEvent>(JoinGame);
|
||||
SubscribeNetworkEvent<TickerConnectionStatusEvent>(ConnectionStatus);
|
||||
@@ -49,33 +53,14 @@ namespace Content.Client.GameTicking.Managers
|
||||
SubscribeNetworkEvent<TickerLobbyInfoEvent>(LobbyInfo);
|
||||
SubscribeNetworkEvent<TickerLobbyCountdownEvent>(LobbyCountdown);
|
||||
SubscribeNetworkEvent<RoundEndMessageEvent>(RoundEnd);
|
||||
SubscribeNetworkEvent<RequestWindowAttentionEvent>(OnAttentionRequest);
|
||||
SubscribeNetworkEvent<RequestWindowAttentionEvent>(msg =>
|
||||
{
|
||||
IoCManager.Resolve<IClyde>().RequestWindowAttention();
|
||||
});
|
||||
SubscribeNetworkEvent<TickerLateJoinStatusEvent>(LateJoinStatus);
|
||||
SubscribeNetworkEvent<TickerJobsAvailableEvent>(UpdateJobsAvailable);
|
||||
|
||||
_admin.AdminStatusUpdated += OnAdminUpdated;
|
||||
OnAdminUpdated();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_admin.AdminStatusUpdated -= OnAdminUpdated;
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
private void OnAdminUpdated()
|
||||
{
|
||||
// Hide some map/grid related logs from clients. This is to try prevent some easy metagaming by just
|
||||
// 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;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnAttentionRequest(RequestWindowAttentionEvent ev)
|
||||
{
|
||||
_clyde.RequestWindowAttention();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private void LateJoinStatus(TickerLateJoinStatusEvent message)
|
||||
@@ -147,7 +132,12 @@ namespace Content.Client.GameTicking.Managers
|
||||
// Force an update in the event of this song being the same as the last.
|
||||
RestartSound = message.RestartSound;
|
||||
|
||||
_userInterfaceManager.GetUIController<RoundEndSummaryUIController>().OpenRoundEndSummaryWindow(message);
|
||||
// Don't open duplicate windows (mainly for replays).
|
||||
if (_window?.RoundId == message.RoundId)
|
||||
return;
|
||||
|
||||
//This is not ideal at all, but I don't see an immediately better fit anywhere else.
|
||||
_window = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundEndText, message.RoundDuration, message.RoundId, message.AllPlayersEndInfo, _entityManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Client.Ghost.Commands;
|
||||
|
||||
public sealed class ToggleGhostVisibilityCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entSysMan = default!;
|
||||
|
||||
public string Command => "toggleghostvisibility";
|
||||
public string Description => "Toggles ghost visibility on the client.";
|
||||
public string Help => "toggleghostvisibility [bool]";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var ghostSystem = _entSysMan.GetEntitySystem<GhostSystem>();
|
||||
|
||||
if (args.Length != 0 && bool.TryParse(args[0], out var visibility))
|
||||
{
|
||||
ghostSystem.ToggleGhostVisibility(visibility);
|
||||
}
|
||||
else
|
||||
{
|
||||
ghostSystem.ToggleGhostVisibility();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Content.Shared.Actions;
|
||||
using Content.Shared.Ghost;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
@@ -176,9 +177,9 @@ namespace Content.Client.Ghost
|
||||
_console.RemoteExecuteCommand(null, "ghostroles");
|
||||
}
|
||||
|
||||
public void ToggleGhostVisibility(bool? visibility = null)
|
||||
public void ToggleGhostVisibility()
|
||||
{
|
||||
GhostVisibility = visibility ?? !GhostVisibility;
|
||||
GhostVisibility = !GhostVisibility;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using Content.Client.Chemistry.EntitySystems;
|
||||
using Content.Client.Guidebook.Richtext;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.UserInterface.ControlExtensions;
|
||||
using Content.Shared.Body.Prototypes;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using JetBrains.Annotations;
|
||||
@@ -129,7 +128,7 @@ public sealed partial class GuideReagentEmbed : BoxContainer, IDocumentTag, ISea
|
||||
|
||||
var groupLabel = new RichTextLabel();
|
||||
groupLabel.SetMarkup(Loc.GetString("guidebook-reagent-effects-metabolism-group-rate",
|
||||
("group", _prototype.Index<MetabolismGroupPrototype>(group).LocalizedName), ("rate", effect.MetabolismRate)));
|
||||
("group", group), ("rate", effect.MetabolismRate)));
|
||||
var descriptionLabel = new RichTextLabel
|
||||
{
|
||||
Margin = new Thickness(25, 0, 10, 0)
|
||||
|
||||
@@ -136,7 +136,6 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
|
||||
TreeItem? parent = forcedRoot == null ? null : AddEntry(forcedRoot, null, addedEntries);
|
||||
foreach (var entry in GetSortedEntries(roots))
|
||||
{
|
||||
if (!entry.CrystallPunkAllowed) continue; //CrystallPunk guidebook filter
|
||||
AddEntry(entry.Id, parent, addedEntries);
|
||||
}
|
||||
Tree.SetAllExpanded(true);
|
||||
|
||||
@@ -39,13 +39,10 @@ public class GuideEntry
|
||||
/// If the guide is the child of some other guide, the order simply determined by the order of children in <see cref="Children"/>.
|
||||
/// </summary>
|
||||
[DataField("priority")] public int Priority = 0;
|
||||
|
||||
[DataField]
|
||||
public bool CrystallPunkAllowed = false;
|
||||
}
|
||||
|
||||
[Prototype("guideEntry")]
|
||||
public sealed partial class GuideEntryPrototype : GuideEntry, IPrototype
|
||||
public sealed class GuideEntryPrototype : GuideEntry, IPrototype
|
||||
{
|
||||
public string ID => Id;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ 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;
|
||||
|
||||
@@ -32,8 +31,6 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
[Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!;
|
||||
[Dependency] private readonly TagSystem _tags = default!;
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!; //CrystallPunk guidebook filter
|
||||
|
||||
public event Action<List<string>, List<string>?, string?, bool, string?>? OnGuidebookOpen;
|
||||
public const string GuideEmbedTag = "GuideEmbeded";
|
||||
|
||||
@@ -42,7 +39,6 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<GuideHelpComponent, MapInitEvent>(OnCrystallPunkMapInit); //CrystallPunk guidebook filter
|
||||
SubscribeLocalEvent<GuideHelpComponent, GetVerbsEvent<ExamineVerb>>(OnGetVerbs);
|
||||
SubscribeLocalEvent<GuideHelpComponent, ActivateInWorldEvent>(OnInteract);
|
||||
|
||||
@@ -52,21 +48,6 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
OnGuidebookControlsTestGetAlternateVerbs);
|
||||
}
|
||||
|
||||
//CrystallPunk guidebook filter
|
||||
private void OnCrystallPunkMapInit(Entity<GuideHelpComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
foreach (var guide in ent.Comp.Guides)
|
||||
{
|
||||
var guideProto = _proto.Index<GuideEntryPrototype>(guide);
|
||||
if (!guideProto.CrystallPunkAllowed) //REMOVE unnecessary guidebook
|
||||
{
|
||||
RemComp<GuideHelpComponent>(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
//CrystallPunk guidebook filter end
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user entity to use for verbs and examinations. If the player has no attached entity, this will use a
|
||||
/// dummy client-side entity so that users can still use the guidebook when not attached to anything (e.g., in the
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
@@ -80,7 +79,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
);
|
||||
|
||||
Temperature.Text = Loc.GetString("health-analyzer-window-entity-temperature-text",
|
||||
("temperature", float.IsNaN(msg.Temperature) ? "N/A" : $"{msg.Temperature - Atmospherics.T0C:F1} °C ({msg.Temperature:F1} K)")
|
||||
("temperature", float.IsNaN(msg.Temperature) ? "N/A" : $"{msg.Temperature - 273f:F1} °C ({msg.Temperature:F1} °K)")
|
||||
);
|
||||
|
||||
BloodLevel.Text = Loc.GetString("health-analyzer-window-entity-blood-level-text",
|
||||
@@ -139,7 +138,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
|
||||
var groupTitleText = $"{Loc.GetString(
|
||||
"health-analyzer-window-damage-group-text",
|
||||
("damageGroup", _prototypes.Index<DamageGroupPrototype>(damageGroupId).LocalizedName),
|
||||
("damageGroup", Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId)),
|
||||
("amount", damageAmount)
|
||||
)}";
|
||||
|
||||
@@ -170,7 +169,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
|
||||
var damageString = Loc.GetString(
|
||||
"health-analyzer-window-damage-type-text",
|
||||
("damageType", _prototypes.Index<DamageTypePrototype>(type).LocalizedName),
|
||||
("damageType", Loc.GetString("health-analyzer-window-damage-type-" + type)),
|
||||
("amount", typeAmount)
|
||||
);
|
||||
|
||||
|
||||
@@ -108,11 +108,8 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||
/// This should not be used if the entity is owned by the server. The server will otherwise
|
||||
/// override this with the appearance data it sends over.
|
||||
/// </remarks>
|
||||
public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile? profile, HumanoidAppearanceComponent? humanoid = null)
|
||||
public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidAppearanceComponent? humanoid = null)
|
||||
{
|
||||
if (profile == null)
|
||||
return;
|
||||
|
||||
if (!Resolve(uid, ref humanoid))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Implants.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -18,7 +17,7 @@ public sealed class ImplanterStatusControl : Control
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
_label.MaxWidth = 350;
|
||||
AddChild(new ClipControl { Children = { _label } });
|
||||
AddChild(_label);
|
||||
|
||||
Update();
|
||||
}
|
||||
@@ -43,12 +42,17 @@ public sealed class ImplanterStatusControl : Control
|
||||
_ => Loc.GetString("injector-invalid-injector-toggle-mode")
|
||||
};
|
||||
|
||||
var implantName = _parent.ImplanterSlot.HasItem
|
||||
? _parent.ImplantData.Item1
|
||||
: Loc.GetString("implanter-empty-text");
|
||||
var (implantName, implantDescription) = _parent.ImplanterSlot.HasItem switch
|
||||
{
|
||||
false => (Loc.GetString("implanter-empty-text"), ""),
|
||||
true => (_parent.ImplantData.Item1, _parent.ImplantData.Item2),
|
||||
};
|
||||
|
||||
|
||||
_label.SetMarkup(Loc.GetString("implanter-label",
|
||||
("implantName", implantName),
|
||||
("modeString", modeStringLocalized)));
|
||||
("implantDescription", implantDescription),
|
||||
("modeString", modeStringLocalized),
|
||||
("lineBreak", "\n")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ namespace Content.Client.Input
|
||||
common.AddFunction(ContentKeyFunctions.ZoomIn);
|
||||
common.AddFunction(ContentKeyFunctions.ResetZoom);
|
||||
common.AddFunction(ContentKeyFunctions.InspectEntity);
|
||||
common.AddFunction(ContentKeyFunctions.ToggleRoundEndSummaryWindow);
|
||||
|
||||
// Not in engine, because engine cannot check for sanbox/admin status before starting placement.
|
||||
common.AddFunction(ContentKeyFunctions.EditorCopyObject);
|
||||
@@ -60,7 +59,6 @@ namespace Content.Client.Input
|
||||
human.AddFunction(ContentKeyFunctions.UseItemInHand);
|
||||
human.AddFunction(ContentKeyFunctions.AltUseItemInHand);
|
||||
human.AddFunction(ContentKeyFunctions.OpenCharacterMenu);
|
||||
human.AddFunction(ContentKeyFunctions.OpenEmotesMenu);
|
||||
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
|
||||
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);
|
||||
human.AddFunction(ContentKeyFunctions.AltActivateItemInWorld);
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace Content.Client.Inventory
|
||||
|
||||
public void UIInventoryStorageActivate(string slot)
|
||||
{
|
||||
EntityManager.RaisePredictiveEvent(new OpenSlotStorageNetworkMessage(slot));
|
||||
EntityManager.EntityNetManager?.SendSystemNetworkMessage(new OpenSlotStorageNetworkMessage(slot));
|
||||
}
|
||||
|
||||
public void UIInventoryExamine(string slot, EntityUid uid)
|
||||
@@ -251,7 +251,6 @@ namespace Content.Client.Inventory
|
||||
public string SlotGroup => SlotDef.SlotGroup;
|
||||
public string SlotDisplayName => SlotDef.DisplayName;
|
||||
public string TextureName => "Slots/" + SlotDef.TextureName;
|
||||
public string FullTextureName => SlotDef.FullTextureName;
|
||||
|
||||
public SlotData(SlotDefinition slotDef, ContainerSlot? container = null, bool highlighted = false,
|
||||
bool blocked = false)
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace Content.Client.Inventory
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
button.SetEntity(null);
|
||||
button.SpriteView.SetEntity(null);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace Content.Client.Inventory
|
||||
else
|
||||
return;
|
||||
|
||||
button.SetEntity(viewEnt);
|
||||
button.SpriteView.SetEntity(viewEnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ using Content.Shared.Module;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Replay;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
|
||||
|
||||
namespace Content.Client.IoC
|
||||
@@ -30,29 +29,26 @@ namespace Content.Client.IoC
|
||||
{
|
||||
public static void Register()
|
||||
{
|
||||
var collection = IoCManager.Instance!;
|
||||
|
||||
collection.Register<IParallaxManager, ParallaxManager>();
|
||||
collection.Register<IChatManager, ChatManager>();
|
||||
collection.Register<IClientPreferencesManager, ClientPreferencesManager>();
|
||||
collection.Register<IStylesheetManager, StylesheetManager>();
|
||||
collection.Register<IScreenshotHook, ScreenshotHook>();
|
||||
collection.Register<FullscreenHook, FullscreenHook>();
|
||||
collection.Register<IClickMapManager, ClickMapManager>();
|
||||
collection.Register<IClientAdminManager, ClientAdminManager>();
|
||||
collection.Register<ISharedAdminManager, ClientAdminManager>();
|
||||
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>();
|
||||
collection.Register<ExtendedDisconnectInformationManager>();
|
||||
collection.Register<JobRequirementsManager>();
|
||||
collection.Register<DocumentParsingManager>();
|
||||
collection.Register<ContentReplayPlaybackManager, ContentReplayPlaybackManager>();
|
||||
collection.Register<ISharedPlaytimeManager, JobRequirementsManager>();
|
||||
IoCManager.Register<IParallaxManager, ParallaxManager>();
|
||||
IoCManager.Register<IChatManager, ChatManager>();
|
||||
IoCManager.Register<IClientPreferencesManager, ClientPreferencesManager>();
|
||||
IoCManager.Register<IStylesheetManager, StylesheetManager>();
|
||||
IoCManager.Register<IScreenshotHook, ScreenshotHook>();
|
||||
IoCManager.Register<FullscreenHook, FullscreenHook>();
|
||||
IoCManager.Register<IClickMapManager, ClickMapManager>();
|
||||
IoCManager.Register<IClientAdminManager, ClientAdminManager>();
|
||||
IoCManager.Register<ISharedAdminManager, ClientAdminManager>();
|
||||
IoCManager.Register<EuiManager, EuiManager>();
|
||||
IoCManager.Register<IVoteManager, VoteManager>();
|
||||
IoCManager.Register<ChangelogManager, ChangelogManager>();
|
||||
IoCManager.Register<RulesManager, RulesManager>();
|
||||
IoCManager.Register<ViewportManager, ViewportManager>();
|
||||
IoCManager.Register<ISharedAdminLogManager, SharedAdminLogManager>();
|
||||
IoCManager.Register<GhostKickManager>();
|
||||
IoCManager.Register<ExtendedDisconnectInformationManager>();
|
||||
IoCManager.Register<JobRequirementsManager>();
|
||||
IoCManager.Register<DocumentParsingManager>();
|
||||
IoCManager.Register<ContentReplayPlaybackManager, ContentReplayPlaybackManager>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Items.UI;
|
||||
|
||||
/// <summary>
|
||||
/// A base for item status controls that poll data every frame. Avoids UI updates if data didn't change.
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">The full status control data that is polled every frame.</typeparam>
|
||||
public abstract class PollingItemStatusControl<TData> : Control where TData : struct, IEquatable<TData>
|
||||
{
|
||||
private TData _lastData;
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
var newData = PollData();
|
||||
if (newData.Equals(_lastData))
|
||||
return;
|
||||
|
||||
_lastData = newData;
|
||||
Update(newData);
|
||||
}
|
||||
|
||||
protected abstract TData PollData();
|
||||
protected abstract void Update(in TData data);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Content.Client.Labels.UI;
|
||||
using Content.Shared.Labels;
|
||||
using Content.Shared.Labels.Components;
|
||||
using Content.Shared.Labels.EntitySystems;
|
||||
|
||||
namespace Content.Client.Labels.EntitySystems;
|
||||
|
||||
public sealed class HandLabelerSystem : SharedHandLabelerSystem
|
||||
{
|
||||
protected override void UpdateUI(Entity<HandLabelerComponent> ent)
|
||||
{
|
||||
if (UserInterfaceSystem.TryGetOpenUi(ent.Owner, HandLabelerUiKey.Key, out var bui)
|
||||
&& bui is HandLabelerBoundUserInterface cBui)
|
||||
{
|
||||
cBui.Reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Shared.Labels;
|
||||
using Content.Shared.Labels.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Labels.UI
|
||||
@@ -9,14 +8,11 @@ namespace Content.Client.Labels.UI
|
||||
/// </summary>
|
||||
public sealed class HandLabelerBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private HandLabelerWindow? _window;
|
||||
|
||||
public HandLabelerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
@@ -31,25 +27,24 @@ namespace Content.Client.Labels.UI
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OnLabelChanged += OnLabelChanged;
|
||||
Reload();
|
||||
}
|
||||
|
||||
private void OnLabelChanged(string newLabel)
|
||||
{
|
||||
// Focus moment
|
||||
if (_entManager.TryGetComponent(Owner, out HandLabelerComponent? labeler) &&
|
||||
labeler.AssignedLabel.Equals(newLabel))
|
||||
return;
|
||||
|
||||
SendPredictedMessage(new HandLabelerLabelChangedMessage(newLabel));
|
||||
SendMessage(new HandLabelerLabelChangedMessage(newLabel));
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
/// <summary>
|
||||
/// Update the UI state based on server-sent info
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
if (_window == null || !_entManager.TryGetComponent(Owner, out HandLabelerComponent? component))
|
||||
base.UpdateState(state);
|
||||
if (_window == null || state is not HandLabelerBoundUserInterfaceState cast)
|
||||
return;
|
||||
|
||||
_window.SetCurrentLabel(component.AssignedLabel);
|
||||
_window.SetCurrentLabel(cast.CurrentLabel);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
@@ -9,40 +9,17 @@ namespace Content.Client.Labels.UI
|
||||
{
|
||||
public event Action<string>? OnLabelChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Is the user currently entering text into the control?
|
||||
/// </summary>
|
||||
private bool _focused;
|
||||
// TODO LineEdit Make this a bool on the LineEdit control
|
||||
|
||||
private string _label = string.Empty;
|
||||
|
||||
public HandLabelerWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
LabelLineEdit.OnTextEntered += e =>
|
||||
{
|
||||
_label = e.Text;
|
||||
OnLabelChanged?.Invoke(_label);
|
||||
};
|
||||
|
||||
LabelLineEdit.OnFocusEnter += _ => _focused = true;
|
||||
LabelLineEdit.OnFocusExit += _ =>
|
||||
{
|
||||
_focused = false;
|
||||
LabelLineEdit.Text = _label;
|
||||
};
|
||||
LabelLineEdit.OnTextEntered += e => OnLabelChanged?.Invoke(e.Text);
|
||||
LabelLineEdit.OnFocusExit += e => OnLabelChanged?.Invoke(e.Text);
|
||||
}
|
||||
|
||||
public void SetCurrentLabel(string label)
|
||||
{
|
||||
if (label == _label)
|
||||
return;
|
||||
|
||||
_label = label;
|
||||
if (!_focused)
|
||||
LabelLineEdit.Text = label;
|
||||
LabelLineEdit.Text = label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Lathe.UI;
|
||||
@@ -21,8 +19,6 @@ public sealed partial class LatheMenu : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IResourceCache _resources = default!;
|
||||
|
||||
private EntityUid _owner;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly LatheSystem _lathe;
|
||||
@@ -108,21 +104,41 @@ public sealed partial class LatheMenu : DefaultWindow
|
||||
RecipeList.Children.Clear();
|
||||
foreach (var prototype in sortedRecipesToShow)
|
||||
{
|
||||
List<Texture> textures;
|
||||
if (_prototypeManager.TryIndex(prototype.Result, out EntityPrototype? entityProto) && entityProto != null)
|
||||
StringBuilder sb = new();
|
||||
var first = true;
|
||||
foreach (var (id, amount) in prototype.RequiredMaterials)
|
||||
{
|
||||
textures = SpriteComponent.GetPrototypeTextures(entityProto, _resources).Select(o => o.Default).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
textures = prototype.Icon == null
|
||||
? new List<Texture> { _spriteSystem.GetPrototypeIcon(prototype.Result).Default }
|
||||
: new List<Texture> { _spriteSystem.Frame0(prototype.Icon) };
|
||||
if (!_prototypeManager.TryIndex<MaterialPrototype>(id, out var proto))
|
||||
continue;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
sb.Append('\n');
|
||||
|
||||
var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, component.MaterialUseMultiplier);
|
||||
var sheetVolume = _materialStorage.GetSheetVolume(proto);
|
||||
|
||||
var unit = Loc.GetString(proto.Unit);
|
||||
// rounded in locale not here
|
||||
var sheets = adjustedAmount / (float) sheetVolume;
|
||||
var amountText = Loc.GetString("lathe-menu-material-amount", ("amount", sheets), ("unit", unit));
|
||||
var name = Loc.GetString(proto.Name);
|
||||
sb.Append(Loc.GetString("lathe-menu-tooltip-display", ("material", name), ("amount", amountText)));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(prototype.Description))
|
||||
{
|
||||
sb.Append('\n');
|
||||
sb.Append(Loc.GetString("lathe-menu-description-display", ("description", prototype.Description)));
|
||||
}
|
||||
|
||||
var icon = prototype.Icon == null
|
||||
? _spriteSystem.GetPrototypeIcon(prototype.Result).Default
|
||||
: _spriteSystem.Frame0(prototype.Icon);
|
||||
var canProduce = _lathe.CanProduce(_owner, prototype, quantity);
|
||||
|
||||
var control = new RecipeControl(prototype, () => GenerateTooltipText(prototype), canProduce, textures);
|
||||
var control = new RecipeControl(prototype, sb.ToString(), canProduce, icon);
|
||||
control.OnButtonPressed += s =>
|
||||
{
|
||||
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0)
|
||||
@@ -133,51 +149,6 @@ public sealed partial class LatheMenu : DefaultWindow
|
||||
}
|
||||
}
|
||||
|
||||
private string GenerateTooltipText(LatheRecipePrototype prototype)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
|
||||
foreach (var (id, amount) in prototype.RequiredMaterials)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<MaterialPrototype>(id, out var proto))
|
||||
continue;
|
||||
|
||||
var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, _entityManager.GetComponent<LatheComponent>(_owner).MaterialUseMultiplier);
|
||||
var sheetVolume = _materialStorage.GetSheetVolume(proto);
|
||||
|
||||
var unit = Loc.GetString(proto.Unit);
|
||||
var sheets = adjustedAmount / (float) sheetVolume;
|
||||
|
||||
var availableAmount = _materialStorage.GetMaterialAmount(_owner, id);
|
||||
var missingAmount = Math.Max(0, adjustedAmount - availableAmount);
|
||||
var missingSheets = missingAmount / (float) sheetVolume;
|
||||
|
||||
var name = Loc.GetString(proto.Name);
|
||||
|
||||
string tooltipText;
|
||||
if (missingSheets > 0)
|
||||
{
|
||||
tooltipText = Loc.GetString("lathe-menu-material-amount-missing", ("amount", sheets), ("missingAmount", missingSheets), ("unit", unit), ("material", name));
|
||||
}
|
||||
else
|
||||
{
|
||||
var amountText = Loc.GetString("lathe-menu-material-amount", ("amount", sheets), ("unit", unit));
|
||||
tooltipText = Loc.GetString("lathe-menu-tooltip-display", ("material", name), ("amount", amountText));
|
||||
}
|
||||
|
||||
sb.AppendLine(tooltipText);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(prototype.Description))
|
||||
sb.AppendLine(Loc.GetString("lathe-menu-description-display", ("description", prototype.Description)));
|
||||
|
||||
// Remove last newline
|
||||
if (sb.Length > 0)
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public void UpdateCategories()
|
||||
{
|
||||
var currentCategories = new List<ProtoId<LatheCategoryPrototype>>();
|
||||
|
||||
@@ -5,15 +5,11 @@
|
||||
Margin="0"
|
||||
StyleClasses="ButtonSquare">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<LayeredTextureRect
|
||||
Name="RecipeTextures"
|
||||
<TextureRect
|
||||
Name="RecipeTexture"
|
||||
Margin="0 0 4 0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Stretch="KeepAspectCentered"
|
||||
MinSize="32 32"
|
||||
CanShrink="true"
|
||||
/>
|
||||
Stretch="KeepAspectCentered" />
|
||||
<Label Name="RecipeName" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
</Button>
|
||||
|
||||
@@ -2,8 +2,8 @@ using Content.Shared.Research.Prototypes;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Graphics;
|
||||
|
||||
namespace Content.Client.Lathe.UI;
|
||||
|
||||
@@ -11,16 +11,17 @@ namespace Content.Client.Lathe.UI;
|
||||
public sealed partial class RecipeControl : Control
|
||||
{
|
||||
public Action<string>? OnButtonPressed;
|
||||
public Func<string> TooltipTextSupplier;
|
||||
|
||||
public RecipeControl(LatheRecipePrototype recipe, Func<string> tooltipTextSupplier, bool canProduce, List<Texture> textures)
|
||||
public string TooltipText;
|
||||
|
||||
public RecipeControl(LatheRecipePrototype recipe, string tooltip, bool canProduce, Texture? texture = null)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
RecipeName.Text = recipe.Name;
|
||||
RecipeTextures.Textures = textures;
|
||||
RecipeTexture.Texture = texture;
|
||||
Button.Disabled = !canProduce;
|
||||
TooltipTextSupplier = tooltipTextSupplier;
|
||||
TooltipText = tooltip;
|
||||
Button.TooltipSupplier = SupplyTooltip;
|
||||
|
||||
Button.OnPressed += (_) =>
|
||||
@@ -31,6 +32,6 @@ public sealed partial class RecipeControl : Control
|
||||
|
||||
private Control? SupplyTooltip(Control sender)
|
||||
{
|
||||
return new RecipeTooltip(TooltipTextSupplier());
|
||||
return new RecipeTooltip(TooltipText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public sealed class ExpendableLightSystem : VisualizerSystem<ExpendableLightComp
|
||||
case ExpendableLightState.Lit:
|
||||
_audioSystem.Stop(comp.PlayingStream);
|
||||
comp.PlayingStream = _audioSystem.PlayPvs(
|
||||
comp.LoopedSound, uid)?.Entity;
|
||||
comp.LoopedSound, uid, SharedExpendableLightComponent.LoopedSoundParams)?.Entity;
|
||||
|
||||
if (args.Sprite.LayerMapTryGet(ExpendableLightVisualLayers.Overlay, out var layerIdx, true))
|
||||
{
|
||||
|
||||
@@ -64,19 +64,13 @@ namespace Content.Client.Lobby
|
||||
|
||||
_characterSetup.CloseButton.OnPressed += _ =>
|
||||
{
|
||||
// Reset sliders etc.
|
||||
_characterSetup?.UpdateControls();
|
||||
|
||||
var controller = _userInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.SetClothes(true);
|
||||
controller.UpdateProfile();
|
||||
_lobby.SwitchState(LobbyGui.LobbyGuiState.Default);
|
||||
};
|
||||
|
||||
_characterSetup.SaveButton.OnPressed += _ =>
|
||||
{
|
||||
_characterSetup.Save();
|
||||
_userInterfaceManager.GetUIController<LobbyUIController>().ReloadProfile();
|
||||
_lobby.CharacterPreview.UpdateUI();
|
||||
};
|
||||
|
||||
LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide);
|
||||
@@ -90,6 +84,10 @@ namespace Content.Client.Lobby
|
||||
_gameTicker.InfoBlobUpdated += UpdateLobbyUi;
|
||||
_gameTicker.LobbyStatusUpdated += LobbyStatusUpdated;
|
||||
_gameTicker.LobbyLateJoinStatusUpdated += LobbyLateJoinStatusUpdated;
|
||||
|
||||
_preferencesManager.OnServerDataLoaded += PreferencesDataLoaded;
|
||||
|
||||
_lobby.CharacterPreview.UpdateUI();
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
@@ -111,6 +109,13 @@ namespace Content.Client.Lobby
|
||||
|
||||
_characterSetup?.Dispose();
|
||||
_characterSetup = null;
|
||||
|
||||
_preferencesManager.OnServerDataLoaded -= PreferencesDataLoaded;
|
||||
}
|
||||
|
||||
private void PreferencesDataLoaded()
|
||||
{
|
||||
_lobby?.CharacterPreview.UpdateUI();
|
||||
}
|
||||
|
||||
private void OnSetupPressed(BaseButton.ButtonEventArgs args)
|
||||
|
||||
@@ -1,286 +0,0 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Inventory;
|
||||
using Content.Client.Lobby.UI;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.Preferences.UI;
|
||||
using Content.Client.Station;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Lobby;
|
||||
|
||||
public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState>, IOnStateExited<LobbyState>
|
||||
{
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[UISystemDependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
||||
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
||||
|
||||
private LobbyCharacterPreviewPanel? _previewPanel;
|
||||
|
||||
private bool _showClothes = true;
|
||||
|
||||
/*
|
||||
* Each character profile has its own dummy. There is also a dummy for the lobby screen + character editor
|
||||
* that is shared too.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Preview dummy for role gear.
|
||||
/// </summary>
|
||||
private EntityUid? _previewDummy;
|
||||
|
||||
/// <summary>
|
||||
/// If we currently have a job prototype selected.
|
||||
/// </summary>
|
||||
private JobPrototype? _dummyJob;
|
||||
|
||||
// TODO: Load the species directly and don't update entity ever.
|
||||
public event Action<EntityUid>? PreviewDummyUpdated;
|
||||
|
||||
private HumanoidCharacterProfile? _profile;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_preferencesManager.OnServerDataLoaded += PreferencesDataLoaded;
|
||||
}
|
||||
|
||||
private void PreferencesDataLoaded()
|
||||
{
|
||||
UpdateProfile();
|
||||
}
|
||||
|
||||
public void OnStateEntered(LobbyState state)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnStateExited(LobbyState state)
|
||||
{
|
||||
EntityManager.DeleteEntity(_previewDummy);
|
||||
_previewDummy = null;
|
||||
}
|
||||
|
||||
public void SetPreviewPanel(LobbyCharacterPreviewPanel? panel)
|
||||
{
|
||||
_previewPanel = panel;
|
||||
ReloadProfile();
|
||||
}
|
||||
|
||||
public void SetClothes(bool value)
|
||||
{
|
||||
if (_showClothes == value)
|
||||
return;
|
||||
|
||||
_showClothes = value;
|
||||
ReloadCharacterUI();
|
||||
}
|
||||
|
||||
public void SetDummyJob(JobPrototype? job)
|
||||
{
|
||||
_dummyJob = job;
|
||||
ReloadCharacterUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the character only with the specified profile change.
|
||||
/// </summary>
|
||||
public void ReloadProfile()
|
||||
{
|
||||
// Test moment
|
||||
if (_profile == null || _stateManager.CurrentState is not LobbyState)
|
||||
return;
|
||||
|
||||
// Ignore job clothes and the likes so we don't spam entities out every frame of color changes.
|
||||
var previewDummy = EnsurePreviewDummy(_profile);
|
||||
_humanoid.LoadProfile(previewDummy, _profile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the currently selected character's preview.
|
||||
/// </summary>
|
||||
public void ReloadCharacterUI()
|
||||
{
|
||||
// Test moment
|
||||
if (_profile == null || _stateManager.CurrentState is not LobbyState)
|
||||
return;
|
||||
|
||||
EntityManager.DeleteEntity(_previewDummy);
|
||||
_previewDummy = null;
|
||||
_previewDummy = EnsurePreviewDummy(_profile);
|
||||
_previewPanel?.SetSprite(_previewDummy.Value);
|
||||
_previewPanel?.SetSummaryText(_profile.Summary);
|
||||
_humanoid.LoadProfile(_previewDummy.Value, _profile);
|
||||
|
||||
if (_showClothes)
|
||||
GiveDummyJobClothesLoadout(_previewDummy.Value, _profile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates character profile to the default.
|
||||
/// </summary>
|
||||
public void UpdateProfile()
|
||||
{
|
||||
if (!_preferencesManager.ServerDataLoaded)
|
||||
{
|
||||
_profile = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_preferencesManager.Preferences?.SelectedCharacter is HumanoidCharacterProfile selectedCharacter)
|
||||
{
|
||||
_profile = selectedCharacter;
|
||||
_previewPanel?.SetLoaded(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_previewPanel?.SetSummaryText(string.Empty);
|
||||
_previewPanel?.SetLoaded(false);
|
||||
}
|
||||
|
||||
ReloadCharacterUI();
|
||||
}
|
||||
|
||||
public void UpdateProfile(HumanoidCharacterProfile? profile)
|
||||
{
|
||||
if (_profile?.Equals(profile) == true)
|
||||
return;
|
||||
|
||||
if (_stateManager.CurrentState is not LobbyState)
|
||||
return;
|
||||
|
||||
_profile = profile;
|
||||
}
|
||||
|
||||
private EntityUid EnsurePreviewDummy(HumanoidCharacterProfile profile)
|
||||
{
|
||||
if (_previewDummy != null)
|
||||
return _previewDummy.Value;
|
||||
|
||||
_previewDummy = EntityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(profile.Species).DollPrototype, MapCoordinates.Nullspace);
|
||||
PreviewDummyUpdated?.Invoke(_previewDummy.Value);
|
||||
return _previewDummy.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the highest priority job's clothes to the dummy.
|
||||
/// </summary>
|
||||
public void GiveDummyJobClothesLoadout(EntityUid dummy, HumanoidCharacterProfile profile)
|
||||
{
|
||||
var job = _dummyJob ?? GetPreferredJob(profile);
|
||||
GiveDummyJobClothes(dummy, profile, job);
|
||||
|
||||
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
|
||||
{
|
||||
var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), EntityManager, _prototypeManager);
|
||||
GiveDummyLoadout(dummy, loadout);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the highest priority job for the profile.
|
||||
/// </summary>
|
||||
public JobPrototype GetPreferredJob(HumanoidCharacterProfile profile)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public void GiveDummyLoadout(EntityUid uid, RoleLoadout? roleLoadout)
|
||||
{
|
||||
if (roleLoadout == null)
|
||||
return;
|
||||
|
||||
foreach (var group in roleLoadout.SelectedLoadouts.Values)
|
||||
{
|
||||
foreach (var loadout in group)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
|
||||
continue;
|
||||
|
||||
_spawn.EquipStartingGear(uid, _prototypeManager.Index(loadoutProto.Equipment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the specified job's clothes to the dummy.
|
||||
/// </summary>
|
||||
public void GiveDummyJobClothes(EntityUid dummy, HumanoidCharacterProfile profile, JobPrototype job)
|
||||
{
|
||||
if (!_inventory.TryGetSlots(dummy, out var slots))
|
||||
return;
|
||||
|
||||
// Apply loadout
|
||||
if (profile.Loadouts.TryGetValue(job.ID, out var jobLoadout))
|
||||
{
|
||||
foreach (var loadouts in jobLoadout.SelectedLoadouts.Values)
|
||||
{
|
||||
foreach (var loadout in loadouts)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
|
||||
continue;
|
||||
|
||||
// TODO: Need some way to apply starting gear to an entity coz holy fucking shit dude.
|
||||
var loadoutGear = _prototypeManager.Index(loadoutProto.Equipment);
|
||||
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var itemType = loadoutGear.GetGear(slot.Name);
|
||||
|
||||
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
|
||||
{
|
||||
EntityManager.DeleteEntity(unequippedItem.Value);
|
||||
}
|
||||
|
||||
if (itemType != string.Empty)
|
||||
{
|
||||
var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
|
||||
_inventory.TryEquip(dummy, item, slot.Name, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (job.StartingGear == null)
|
||||
return;
|
||||
|
||||
var gear = _prototypeManager.Index<StartingGearPrototype>(job.StartingGear);
|
||||
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var itemType = gear.GetGear(slot.Name);
|
||||
|
||||
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
|
||||
{
|
||||
EntityManager.DeleteEntity(unequippedItem.Value);
|
||||
}
|
||||
|
||||
if (itemType != string.Empty)
|
||||
{
|
||||
var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
|
||||
_inventory.TryEquip(dummy, item, slot.Name, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EntityUid? GetPreviewDummy()
|
||||
{
|
||||
return _previewDummy;
|
||||
}
|
||||
}
|
||||
166
Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.cs
Normal file
166
Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Alerts;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Inventory;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
public sealed class LobbyCharacterPreviewPanel : Control
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
|
||||
private EntityUid? _previewDummy;
|
||||
private readonly Label _summaryLabel;
|
||||
private readonly BoxContainer _loaded;
|
||||
private readonly BoxContainer _viewBox;
|
||||
private readonly Label _unloaded;
|
||||
|
||||
public LobbyCharacterPreviewPanel()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
var header = new NanoHeading
|
||||
{
|
||||
Text = Loc.GetString("lobby-character-preview-panel-header")
|
||||
};
|
||||
|
||||
CharacterSetupButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("lobby-character-preview-panel-character-setup-button"),
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Margin = new Thickness(0, 5, 0, 0),
|
||||
};
|
||||
|
||||
_summaryLabel = new Label
|
||||
{
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Margin = new Thickness(3, 3),
|
||||
};
|
||||
|
||||
var vBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
_unloaded = new Label { Text = Loc.GetString("lobby-character-preview-panel-unloaded-preferences-label") };
|
||||
|
||||
_loaded = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Visible = false
|
||||
};
|
||||
_viewBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
};
|
||||
var _vSpacer = new VSpacer();
|
||||
|
||||
_loaded.AddChild(_summaryLabel);
|
||||
_loaded.AddChild(_viewBox);
|
||||
_loaded.AddChild(_vSpacer);
|
||||
_loaded.AddChild(CharacterSetupButton);
|
||||
|
||||
vBox.AddChild(header);
|
||||
vBox.AddChild(_loaded);
|
||||
vBox.AddChild(_unloaded);
|
||||
AddChild(vBox);
|
||||
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public Button CharacterSetupButton { get; }
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (!disposing) return;
|
||||
if (_previewDummy != null) _entityManager.DeleteEntity(_previewDummy.Value);
|
||||
_previewDummy = default;
|
||||
}
|
||||
|
||||
public void UpdateUI()
|
||||
{
|
||||
if (!_preferencesManager.ServerDataLoaded)
|
||||
{
|
||||
_loaded.Visible = false;
|
||||
_unloaded.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_loaded.Visible = true;
|
||||
_unloaded.Visible = false;
|
||||
if (_preferencesManager.Preferences?.SelectedCharacter is not HumanoidCharacterProfile selectedCharacter)
|
||||
{
|
||||
_summaryLabel.Text = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
_previewDummy = _entityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(selectedCharacter.Species).DollPrototype, MapCoordinates.Nullspace);
|
||||
_viewBox.DisposeAllChildren();
|
||||
var spriteView = new SpriteView
|
||||
{
|
||||
OverrideDirection = Direction.South,
|
||||
Scale = new Vector2(4f, 4f),
|
||||
MaxSize = new Vector2(112, 112),
|
||||
Stretch = SpriteView.StretchMode.Fill,
|
||||
};
|
||||
spriteView.SetEntity(_previewDummy.Value);
|
||||
_viewBox.AddChild(spriteView);
|
||||
_summaryLabel.Text = selectedCharacter.Summary;
|
||||
_entityManager.System<HumanoidAppearanceSystem>().LoadProfile(_previewDummy.Value, selectedCharacter);
|
||||
GiveDummyJobClothes(_previewDummy.Value, selectedCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void GiveDummyJobClothes(EntityUid dummy, HumanoidCharacterProfile profile)
|
||||
{
|
||||
var protoMan = IoCManager.Resolve<IPrototypeManager>();
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
var invSystem = EntitySystem.Get<ClientInventorySystem>();
|
||||
|
||||
var highPriorityJob = profile.JobPriorities.FirstOrDefault(p => p.Value == JobPriority.High).Key;
|
||||
|
||||
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?)
|
||||
var job = protoMan.Index<JobPrototype>(highPriorityJob ?? SharedGameTicker.FallbackOverflowJob);
|
||||
|
||||
if (job.StartingGear != null && invSystem.TryGetSlots(dummy, out var slots))
|
||||
{
|
||||
var gear = protoMan.Index<StartingGearPrototype>(job.StartingGear);
|
||||
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var itemType = gear.GetGear(slot.Name, profile);
|
||||
|
||||
if (invSystem.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
|
||||
{
|
||||
entMan.DeleteEntity(unequippedItem.Value);
|
||||
}
|
||||
|
||||
if (itemType != string.Empty)
|
||||
{
|
||||
var item = entMan.SpawnEntity(itemType, MapCoordinates.Nullspace);
|
||||
invSystem.TryEquip(dummy, item, slot.Name, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<Control
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
<BoxContainer Name="VBox" Orientation="Vertical">
|
||||
<controls:NanoHeading Name="Header" Text="{Loc 'lobby-character-preview-panel-header'}">
|
||||
|
||||
</controls:NanoHeading>
|
||||
<BoxContainer Name="Loaded" Orientation="Vertical"
|
||||
Visible="False">
|
||||
<Label Name="Summary" HorizontalAlignment="Center" Margin="3 3"/>
|
||||
<BoxContainer Name="ViewBox" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
|
||||
</BoxContainer>
|
||||
<controls:VSpacer/>
|
||||
<Button Name="CharacterSetup" Text="{Loc 'lobby-character-preview-panel-character-setup-button'}"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0 5 0 0"/>
|
||||
</BoxContainer>
|
||||
<Label Name="Unloaded" Text="{Loc 'lobby-character-preview-panel-unloaded-preferences-label'}"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
@@ -1,45 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Lobby.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LobbyCharacterPreviewPanel : Control
|
||||
{
|
||||
public Button CharacterSetupButton => CharacterSetup;
|
||||
|
||||
public LobbyCharacterPreviewPanel()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
UserInterfaceManager.GetUIController<LobbyUIController>().SetPreviewPanel(this);
|
||||
}
|
||||
|
||||
public void SetLoaded(bool value)
|
||||
{
|
||||
Loaded.Visible = value;
|
||||
Unloaded.Visible = !value;
|
||||
}
|
||||
|
||||
public void SetSummaryText(string value)
|
||||
{
|
||||
Summary.Text = string.Empty;
|
||||
}
|
||||
|
||||
public void SetSprite(EntityUid uid)
|
||||
{
|
||||
ViewBox.DisposeAllChildren();
|
||||
var spriteView = new SpriteView
|
||||
{
|
||||
OverrideDirection = Direction.South,
|
||||
Scale = new Vector2(4f, 4f),
|
||||
MaxSize = new Vector2(112, 112),
|
||||
Stretch = SpriteView.StretchMode.Fill,
|
||||
};
|
||||
spriteView.SetEntity(uid);
|
||||
ViewBox.AddChild(spriteView);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,23 @@
|
||||
using Content.Client.Chat.UI;
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.Preferences.UI;
|
||||
using Content.Client.UserInterface.Screens;
|
||||
using Content.Client.UserInterface.Systems.Chat.Widgets;
|
||||
using Content.Client.UserInterface.Systems.EscapeMenu;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
|
||||
@@ -72,6 +72,9 @@ public sealed class MagicMirrorBoundUserInterface : BoundUserInterface
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_window != null)
|
||||
_window.OnClose -= Close;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
using Content.Shared.MagicMirror;
|
||||
|
||||
namespace Content.Client.MagicMirror;
|
||||
|
||||
public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -210,9 +210,9 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
||||
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "dead");
|
||||
}
|
||||
|
||||
else if (sensor.DamagePercentage != null)
|
||||
else if (sensor.TotalDamage != null)
|
||||
{
|
||||
var index = MathF.Round(4f * sensor.DamagePercentage.Value);
|
||||
var index = MathF.Round(4f * (sensor.TotalDamage.Value / 100f));
|
||||
|
||||
if (index >= 5)
|
||||
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "critical");
|
||||
|
||||
@@ -49,7 +49,7 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem
|
||||
if (angleDir == curRot.GetCardinalDir())
|
||||
return;
|
||||
|
||||
RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
|
||||
RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
|
||||
{
|
||||
Direction = angleDir,
|
||||
});
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
using System.Numerics;
|
||||
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 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
|
||||
{
|
||||
[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);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StunnedEvent>(OnStunned);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, KnockedDownEvent>(OnKnockedDown);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, BuckleChangeEvent>(OnBuckleChange);
|
||||
}
|
||||
|
||||
private void OnMovementInput(EntityUid entity, WaddleAnimationComponent component, MoveInputEvent 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);
|
||||
}
|
||||
|
||||
private void OnStartedWalking(EntityUid uid, WaddleAnimationComponent component, StartedWaddlingEvent args)
|
||||
{
|
||||
if (_animation.HasRunningAnimation(uid, component.KeyName))
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(uid, out var mover))
|
||||
return;
|
||||
|
||||
if (_gravity.IsWeightless(uid))
|
||||
return;
|
||||
|
||||
|
||||
if (!_actionBlocker.CanMove(uid, mover))
|
||||
return;
|
||||
|
||||
// Do nothing if buckled in
|
||||
if (_buckle.IsBuckled(uid))
|
||||
return;
|
||||
|
||||
// Do nothing if crit or dead (for obvious reasons)
|
||||
if (_mobState.IsIncapacitated(uid))
|
||||
return;
|
||||
|
||||
var tumbleIntensity = component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity;
|
||||
var len = mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength;
|
||||
|
||||
component.LastStep = !component.LastStep;
|
||||
component.IsCurrentlyWaddling = true;
|
||||
|
||||
var anim = new Animation()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(len),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty()
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Rotation),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(0), 0),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(tumbleIntensity), len/2),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(0), len/2),
|
||||
}
|
||||
},
|
||||
new AnimationTrackComponentProperty()
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Offset),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), 0),
|
||||
new AnimationTrackProperty.KeyFrame(component.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;
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,6 @@ public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
|
||||
_linkMenu?.Dispose();
|
||||
_listMenu?.Dispose();
|
||||
_configurationMenu?.Dispose();
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
|
||||
namespace Content.Client.Nutrition.EntitySystems;
|
||||
|
||||
public sealed class DrinkSystem : SharedDrinkSystem
|
||||
{
|
||||
}
|
||||
@@ -36,9 +36,6 @@
|
||||
<CheckBox Name="IntegerScalingCheckBox"
|
||||
Text="{Loc 'ui-options-vp-integer-scaling'}"
|
||||
ToolTip="{Loc 'ui-options-vp-integer-scaling-tooltip'}" />
|
||||
<CheckBox Name="ViewportVerticalFitCheckBox"
|
||||
Text="{Loc 'ui-options-vp-vertical-fit'}"
|
||||
ToolTip="{Loc 'ui-options-vp-vertical-fit-tooltip'}" />
|
||||
<CheckBox Name="ViewportLowResCheckBox" Text="{Loc 'ui-options-vp-low-res'}" />
|
||||
<CheckBox Name="ParallaxLowQualityCheckBox" Text="{Loc 'ui-options-parallax-low-quality'}" />
|
||||
<CheckBox Name="FpsCounterCheckBox" Text="{Loc 'ui-options-fps-counter'}" />
|
||||
|
||||
@@ -67,12 +67,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
UpdateApplyButton();
|
||||
};
|
||||
|
||||
ViewportVerticalFitCheckBox.OnToggled += _ =>
|
||||
{
|
||||
UpdateViewportScale();
|
||||
UpdateApplyButton();
|
||||
};
|
||||
|
||||
IntegerScalingCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
ViewportLowResCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
ParallaxLowQualityCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
@@ -85,7 +79,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
ViewportScaleSlider.Value = _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
|
||||
ViewportStretchCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportStretch);
|
||||
IntegerScalingCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0;
|
||||
ViewportVerticalFitCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportVerticalFit);
|
||||
ViewportLowResCheckBox.Pressed = !_cfg.GetCVar(CCVars.ViewportScaleRender);
|
||||
ParallaxLowQualityCheckBox.Pressed = _cfg.GetCVar(CCVars.ParallaxLowQuality);
|
||||
FpsCounterCheckBox.Pressed = _cfg.GetCVar(CCVars.HudFpsCounterVisible);
|
||||
@@ -118,7 +111,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
_cfg.SetCVar(CCVars.ViewportFixedScaleFactor, (int) ViewportScaleSlider.Value);
|
||||
_cfg.SetCVar(CCVars.ViewportSnapToleranceMargin,
|
||||
IntegerScalingCheckBox.Pressed ? CCVars.ViewportSnapToleranceMargin.DefaultValue : 0);
|
||||
_cfg.SetCVar(CCVars.ViewportVerticalFit, ViewportVerticalFitCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.ViewportScaleRender, !ViewportLowResCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.ParallaxLowQuality, ParallaxLowQualityCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.HudFpsCounterVisible, FpsCounterCheckBox.Pressed);
|
||||
@@ -148,7 +140,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
var isVPStretchSame = ViewportStretchCheckBox.Pressed == _cfg.GetCVar(CCVars.ViewportStretch);
|
||||
var isVPScaleSame = (int) ViewportScaleSlider.Value == _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
|
||||
var isIntegerScalingSame = IntegerScalingCheckBox.Pressed == (_cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0);
|
||||
var isVPVerticalFitSame = ViewportVerticalFitCheckBox.Pressed == _cfg.GetCVar(CCVars.ViewportVerticalFit);
|
||||
var isVPResSame = ViewportLowResCheckBox.Pressed == !_cfg.GetCVar(CCVars.ViewportScaleRender);
|
||||
var isPLQSame = ParallaxLowQualityCheckBox.Pressed == _cfg.GetCVar(CCVars.ParallaxLowQuality);
|
||||
var isFpsCounterVisibleSame = FpsCounterCheckBox.Pressed == _cfg.GetCVar(CCVars.HudFpsCounterVisible);
|
||||
@@ -161,7 +152,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
isVPStretchSame &&
|
||||
isVPScaleSame &&
|
||||
isIntegerScalingSame &&
|
||||
isVPVerticalFitSame &&
|
||||
isVPResSame &&
|
||||
isPLQSame &&
|
||||
isFpsCounterVisibleSame &&
|
||||
@@ -245,8 +235,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
{
|
||||
ViewportScaleBox.Visible = !ViewportStretchCheckBox.Pressed;
|
||||
IntegerScalingCheckBox.Visible = ViewportStretchCheckBox.Pressed;
|
||||
ViewportVerticalFitCheckBox.Visible = ViewportStretchCheckBox.Pressed;
|
||||
ViewportWidthSlider.Visible = ViewportWidthSliderDisplay.Visible = !ViewportStretchCheckBox.Pressed || ViewportStretchCheckBox.Pressed && !ViewportVerticalFitCheckBox.Pressed;
|
||||
ViewportScaleText.Text = Loc.GetString("ui-options-vp-scale", ("scale", ViewportScaleSlider.Value));
|
||||
}
|
||||
|
||||
|
||||
@@ -215,7 +215,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AddButton(ContentKeyFunctions.OpenInventoryMenu);
|
||||
AddButton(ContentKeyFunctions.OpenAHelp);
|
||||
AddButton(ContentKeyFunctions.OpenActionsMenu);
|
||||
AddButton(ContentKeyFunctions.ToggleRoundEndSummaryWindow);
|
||||
AddButton(ContentKeyFunctions.OpenEntitySpawnWindow);
|
||||
AddButton(ContentKeyFunctions.OpenSandboxWindow);
|
||||
AddButton(ContentKeyFunctions.OpenTileSpawnWindow);
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
<Label Text="{Loc 'ui-options-general-speech'}"
|
||||
FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
|
||||
StyleClasses="LabelKeyText"/>
|
||||
<CheckBox Name="ShowOocPatronColor" Text="{Loc 'ui-options-show-ooc-patron-color'}" />
|
||||
<CheckBox Name="ShowLoocAboveHeadCheckBox" Text="{Loc 'ui-options-show-looc-on-head'}" />
|
||||
<CheckBox Name="FancySpeechBubblesCheckBox" Text="{Loc 'ui-options-fancy-speech'}" />
|
||||
<CheckBox Name="FancyNameBackgroundsCheckBox" Text="{Loc 'ui-options-fancy-name-background'}" />
|
||||
|
||||
@@ -3,14 +3,11 @@ using Content.Client.UserInterface.Screens;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.HUD;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Range = Robust.Client.UserInterface.Controls.Range;
|
||||
|
||||
@@ -19,7 +16,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class MiscTab : Control
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
@@ -59,13 +55,8 @@ namespace Content.Client.Options.UI.Tabs
|
||||
UpdateApplyButton();
|
||||
};
|
||||
|
||||
// Channel can be null in replays so.
|
||||
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
|
||||
ShowOocPatronColor.Visible = _playerManager.LocalSession?.Channel?.UserData.PatronTier is { };
|
||||
|
||||
HudThemeOption.OnItemSelected += OnHudThemeChanged;
|
||||
DiscordRich.OnToggled += OnCheckBoxToggled;
|
||||
ShowOocPatronColor.OnToggled += OnCheckBoxToggled;
|
||||
ShowLoocAboveHeadCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
ShowHeldItemCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
ShowCombatModeIndicatorsCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
@@ -82,7 +73,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
|
||||
HudThemeOption.SelectId(_hudThemeIdToIndex.GetValueOrDefault(_cfg.GetCVar(CVars.InterfaceTheme), 0));
|
||||
DiscordRich.Pressed = _cfg.GetCVar(CVars.DiscordEnabled);
|
||||
ShowOocPatronColor.Pressed = _cfg.GetCVar(CCVars.ShowOocPatronColor);
|
||||
ShowLoocAboveHeadCheckBox.Pressed = _cfg.GetCVar(CCVars.LoocAboveHeadShow);
|
||||
ShowHeldItemCheckBox.Pressed = _cfg.GetCVar(CCVars.HudHeldItemShow);
|
||||
ShowCombatModeIndicatorsCheckBox.Pressed = _cfg.GetCVar(CCVars.CombatModeIndicatorsPointShow);
|
||||
@@ -140,7 +130,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
_cfg.SetCVar(CCVars.HudHeldItemShow, ShowHeldItemCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.CombatModeIndicatorsPointShow, ShowCombatModeIndicatorsCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.OpaqueStorageWindow, OpaqueStorageWindowCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.ShowOocPatronColor, ShowOocPatronColor.Pressed);
|
||||
_cfg.SetCVar(CCVars.LoocAboveHeadShow, ShowLoocAboveHeadCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.ChatEnableFancyBubbles, FancySpeechBubblesCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.ChatFancyNameBackground, FancyNameBackgroundsCheckBox.Pressed);
|
||||
@@ -169,7 +158,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
var isShowHeldItemSame = ShowHeldItemCheckBox.Pressed == _cfg.GetCVar(CCVars.HudHeldItemShow);
|
||||
var isCombatModeIndicatorsSame = ShowCombatModeIndicatorsCheckBox.Pressed == _cfg.GetCVar(CCVars.CombatModeIndicatorsPointShow);
|
||||
var isOpaqueStorageWindow = OpaqueStorageWindowCheckBox.Pressed == _cfg.GetCVar(CCVars.OpaqueStorageWindow);
|
||||
var isOocPatronColorShowSame = ShowOocPatronColor.Pressed == _cfg.GetCVar(CCVars.ShowOocPatronColor);
|
||||
var isLoocShowSame = ShowLoocAboveHeadCheckBox.Pressed == _cfg.GetCVar(CCVars.LoocAboveHeadShow);
|
||||
var isFancyChatSame = FancySpeechBubblesCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatEnableFancyBubbles);
|
||||
var isFancyBackgroundSame = FancyNameBackgroundsCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatFancyNameBackground);
|
||||
@@ -187,7 +175,6 @@ namespace Content.Client.Options.UI.Tabs
|
||||
isShowHeldItemSame &&
|
||||
isCombatModeIndicatorsSame &&
|
||||
isOpaqueStorageWindow &&
|
||||
isOocPatronColorShowSame &&
|
||||
isLoocShowSame &&
|
||||
isFancyChatSame &&
|
||||
isFancyBackgroundSame &&
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Content.Client.Overlays;
|
||||
/// </summary>
|
||||
public sealed class EntityHealthBarOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
private readonly MobStateSystem _mobStateSystem;
|
||||
@@ -26,14 +27,17 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
private readonly ProgressColorSystem _progressColor;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
public HashSet<string> DamageContainers = new();
|
||||
private readonly ShaderInstance _shader;
|
||||
|
||||
public EntityHealthBarOverlay(IEntityManager entManager)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_entManager = entManager;
|
||||
_transform = _entManager.System<SharedTransformSystem>();
|
||||
_mobStateSystem = _entManager.System<MobStateSystem>();
|
||||
_mobThresholdSystem = _entManager.System<MobThresholdSystem>();
|
||||
_progressColor = _entManager.System<ProgressColorSystem>();
|
||||
_shader = _prototype.Index<ShaderPrototype>("unshaded").Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
@@ -46,6 +50,8 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3.CreateRotation(-rotation);
|
||||
|
||||
handle.UseShader(_shader);
|
||||
|
||||
var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>();
|
||||
while (query.MoveNext(out var uid,
|
||||
out var mobThresholdsComponent,
|
||||
@@ -116,6 +122,7 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
handle.DrawRect(pixelDarken, Black.WithAlpha(128));
|
||||
}
|
||||
|
||||
handle.UseShader(null);
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.Security.Components;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowCriminalRecordIconsSystem : EquipmentHudSystem<ShowCriminalRecordIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CriminalRecordComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, CriminalRecordComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.StatusIcon.Id, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowHungerIconsSystem : EquipmentHudSystem<ShowHungerIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly HungerSystem _hunger = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -16,12 +17,42 @@ public sealed class ShowHungerIconsSystem : EquipmentHudSystem<ShowHungerIconsCo
|
||||
SubscribeLocalEvent<HungerComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent component, ref GetStatusIconsEvent ev)
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent hungerComponent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive || args.InContainer)
|
||||
return;
|
||||
|
||||
if (_hunger.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
var hungerIcons = DecideHungerIcon(uid, hungerComponent);
|
||||
|
||||
args.StatusIcons.AddRange(hungerIcons);
|
||||
}
|
||||
|
||||
private IReadOnlyList<StatusIconPrototype> DecideHungerIcon(EntityUid uid, HungerComponent hungerComponent)
|
||||
{
|
||||
var result = new List<StatusIconPrototype>();
|
||||
|
||||
switch (hungerComponent.CurrentThreshold)
|
||||
{
|
||||
case HungerThreshold.Overfed:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("HungerIconOverfed", out var overfed))
|
||||
{
|
||||
result.Add(overfed);
|
||||
}
|
||||
break;
|
||||
case HungerThreshold.Peckish:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("HungerIconPeckish", out var peckish))
|
||||
{
|
||||
result.Add(peckish);
|
||||
}
|
||||
break;
|
||||
case HungerThreshold.Starving:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("HungerIconStarving", out var starving))
|
||||
{
|
||||
result.Add(starving);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
|
||||
[ValidatePrototypeId<StatusIconPrototype>]
|
||||
private const string JobIconForNoId = "JobIconNoId";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StatusIconComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
|
||||
var iconId = JobIconForNoId;
|
||||
|
||||
if (_accessReader.FindAccessItemsInventory(uid, out var items))
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
// ID Card
|
||||
if (TryComp<IdCardComponent>(item, out var id))
|
||||
{
|
||||
iconId = id.JobIcon;
|
||||
break;
|
||||
}
|
||||
|
||||
// PDA
|
||||
if (TryComp<PdaComponent>(item, out var pda)
|
||||
&& pda.ContainedId != null
|
||||
&& TryComp(pda.ContainedId, out id))
|
||||
{
|
||||
iconId = id.JobIcon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(iconId, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
else
|
||||
Log.Error($"Invalid job icon prototype: {iconPrototype}");
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowMindShieldIconsSystem : EquipmentHudSystem<ShowMindShieldIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MindShieldComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.MindShieldStatusIcon.Id, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
86
Content.Client/Overlays/ShowSecurityIconsSystem.cs
Normal file
86
Content.Client/Overlays/ShowSecurityIconsSystem.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Security.Components;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowSecurityIconsSystem : EquipmentHudSystem<ShowSecurityIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
|
||||
[ValidatePrototypeId<StatusIconPrototype>]
|
||||
private const string JobIconForNoId = "JobIconNoId";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StatusIconComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent @event)
|
||||
{
|
||||
if (!IsActive || @event.InContainer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var securityIcons = DecideSecurityIcon(uid);
|
||||
|
||||
@event.StatusIcons.AddRange(securityIcons);
|
||||
}
|
||||
|
||||
private IReadOnlyList<StatusIconPrototype> DecideSecurityIcon(EntityUid uid)
|
||||
{
|
||||
var result = new List<StatusIconPrototype>();
|
||||
|
||||
var jobIconToGet = JobIconForNoId;
|
||||
if (_accessReader.FindAccessItemsInventory(uid, out var items))
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
// ID Card
|
||||
if (TryComp(item, out IdCardComponent? id))
|
||||
{
|
||||
jobIconToGet = id.JobIcon;
|
||||
break;
|
||||
}
|
||||
|
||||
// PDA
|
||||
if (TryComp(item, out PdaComponent? pda)
|
||||
&& pda.ContainedId != null
|
||||
&& TryComp(pda.ContainedId, out id))
|
||||
{
|
||||
jobIconToGet = id.JobIcon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>(jobIconToGet, out var jobIcon))
|
||||
result.Add(jobIcon);
|
||||
else
|
||||
Log.Error($"Invalid job icon prototype: {jobIcon}");
|
||||
|
||||
if (TryComp<MindShieldComponent>(uid, out var comp))
|
||||
{
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>(comp.MindShieldStatusIcon.Id, out var icon))
|
||||
result.Add(icon);
|
||||
}
|
||||
|
||||
if (TryComp<CriminalRecordComponent>(uid, out var record))
|
||||
{
|
||||
if(_prototypeMan.TryIndex<StatusIconPrototype>(record.StatusIcon.Id, out var criminalIcon))
|
||||
result.Add(criminalIcon);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.NukeOps;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowSyndicateIconsSystem : EquipmentHudSystem<ShowSyndicateIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
@@ -17,13 +16,28 @@ public sealed class ShowSyndicateIconsSystem : EquipmentHudSystem<ShowSyndicateI
|
||||
SubscribeLocalEvent<NukeOperativeComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent component, ref GetStatusIconsEvent ev)
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent nukeOperativeComponent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive || args.InContainer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.SyndStatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
var syndicateIcons = SyndicateIcon(uid, nukeOperativeComponent);
|
||||
|
||||
args.StatusIcons.AddRange(syndicateIcons);
|
||||
}
|
||||
|
||||
private IReadOnlyList<StatusIconPrototype> SyndicateIcon(EntityUid uid, NukeOperativeComponent nukeOperativeComponent)
|
||||
{
|
||||
var result = new List<StatusIconPrototype>();
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(nukeOperativeComponent.SyndStatusIcon, out var syndicateicon))
|
||||
{
|
||||
result.Add(syndicateicon);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly ThirstSystem _thirst = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -16,12 +17,42 @@ public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsCo
|
||||
SubscribeLocalEvent<ThirstComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent component, ref GetStatusIconsEvent ev)
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent thirstComponent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive || args.InContainer)
|
||||
return;
|
||||
|
||||
if (_thirst.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype!);
|
||||
var thirstIcons = DecideThirstIcon(uid, thirstComponent);
|
||||
|
||||
args.StatusIcons.AddRange(thirstIcons);
|
||||
}
|
||||
|
||||
private IReadOnlyList<StatusIconPrototype> DecideThirstIcon(EntityUid uid, ThirstComponent thirstComponent)
|
||||
{
|
||||
var result = new List<StatusIconPrototype>();
|
||||
|
||||
switch (thirstComponent.CurrentThirstThreshold)
|
||||
{
|
||||
case ThirstThreshold.OverHydrated:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("ThirstIconOverhydrated", out var overhydrated))
|
||||
{
|
||||
result.Add(overhydrated);
|
||||
}
|
||||
break;
|
||||
case ThirstThreshold.Thirsty:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("ThirstIconThirsty", out var thirsty))
|
||||
{
|
||||
result.Add(thirsty);
|
||||
}
|
||||
break;
|
||||
case ThirstThreshold.Parched:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("ThirstIconParched", out var parched))
|
||||
{
|
||||
result.Add(parched);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Content.Client.PDA
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
SendMessage(new PdaRequestUpdateInterfaceMessage());
|
||||
_menu = new PdaMenu();
|
||||
_menu.OpenCenteredLeft();
|
||||
_menu.OnClose += Close;
|
||||
@@ -31,17 +32,17 @@ namespace Content.Client.PDA
|
||||
|
||||
_menu.EjectIdButton.OnPressed += _ =>
|
||||
{
|
||||
SendPredictedMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaIdSlotId));
|
||||
SendMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaIdSlotId));
|
||||
};
|
||||
|
||||
_menu.EjectPenButton.OnPressed += _ =>
|
||||
{
|
||||
SendPredictedMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPenSlotId));
|
||||
SendMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPenSlotId));
|
||||
};
|
||||
|
||||
_menu.EjectPaiButton.OnPressed += _ =>
|
||||
{
|
||||
SendPredictedMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPaiSlotId));
|
||||
SendMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPaiSlotId));
|
||||
};
|
||||
|
||||
_menu.ActivateMusicButton.OnPressed += _ =>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using Content.Shared.Paper;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Paper;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class PaperComponent : SharedPaperComponent;
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class PaperComponent : SharedPaperComponent
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Content.Client.Pinpointer;
|
||||
|
||||
public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
public sealed class NavMapSystem : SharedNavMapSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NavMapComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
|
||||
@@ -17,34 +21,89 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
if (args.Current is not NavMapComponentState state)
|
||||
return;
|
||||
|
||||
if (!state.FullState)
|
||||
{
|
||||
foreach (var index in component.Chunks.Keys)
|
||||
{
|
||||
if (!state.AllChunks!.Contains(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var index in component.Chunks.Keys)
|
||||
{
|
||||
if (!state.Chunks.ContainsKey(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
}
|
||||
component.Chunks.Clear();
|
||||
|
||||
foreach (var (origin, chunk) in state.Chunks)
|
||||
foreach (var (origin, data) in state.TileData)
|
||||
{
|
||||
var newChunk = new NavMapChunk(origin);
|
||||
Array.Copy(chunk, newChunk.TileData, chunk.Length);
|
||||
component.Chunks[origin] = newChunk;
|
||||
component.Chunks.Add(origin, new NavMapChunk(origin)
|
||||
{
|
||||
TileData = data,
|
||||
});
|
||||
}
|
||||
|
||||
component.Beacons.Clear();
|
||||
foreach (var (nuid, beacon) in state.Beacons)
|
||||
{
|
||||
component.Beacons[nuid] = beacon;
|
||||
}
|
||||
component.Beacons.AddRange(state.Beacons);
|
||||
|
||||
component.Airlocks.Clear();
|
||||
component.Airlocks.AddRange(state.Airlocks);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NavMapOverlay : Overlay
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IMapManager _mapManager;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
public NavMapOverlay(IEntityManager entManager, IMapManager mapManager)
|
||||
{
|
||||
_entManager = entManager;
|
||||
_mapManager = mapManager;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var query = _entManager.GetEntityQuery<NavMapComponent>();
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
var scale = Matrix3.CreateScale(new Vector2(1f, 1f));
|
||||
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(args.MapId, args.WorldBounds, ref _grids);
|
||||
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
if (!query.TryGetComponent(grid, out var navMap) || !xformQuery.TryGetComponent(grid.Owner, out var xform))
|
||||
continue;
|
||||
|
||||
// TODO: Faster helper method
|
||||
var (_, _, matrix, invMatrix) = xform.GetWorldPositionRotationMatrixWithInv();
|
||||
|
||||
var localAABB = invMatrix.TransformBox(args.WorldBounds);
|
||||
Matrix3.Multiply(in scale, in matrix, out var matty);
|
||||
|
||||
args.WorldHandle.SetTransform(matty);
|
||||
|
||||
for (var x = Math.Floor(localAABB.Left); x <= Math.Ceiling(localAABB.Right); x += SharedNavMapSystem.ChunkSize * grid.Comp.TileSize)
|
||||
{
|
||||
for (var y = Math.Floor(localAABB.Bottom); y <= Math.Ceiling(localAABB.Top); y += SharedNavMapSystem.ChunkSize * grid.Comp.TileSize)
|
||||
{
|
||||
var floored = new Vector2i((int) x, (int) y);
|
||||
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(floored, SharedNavMapSystem.ChunkSize);
|
||||
|
||||
if (!navMap.Chunks.TryGetValue(chunkOrigin, out var chunk))
|
||||
continue;
|
||||
|
||||
// TODO: Okay maybe I should just use ushorts lmao...
|
||||
for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
|
||||
{
|
||||
var value = (int) Math.Pow(2, i);
|
||||
|
||||
var mask = chunk.TileData & value;
|
||||
|
||||
if (mask == 0x0)
|
||||
continue;
|
||||
|
||||
var tile = chunk.Origin * SharedNavMapSystem.ChunkSize + SharedNavMapSystem.GetTile(mask);
|
||||
args.WorldHandle.DrawRect(new Box2(tile * grid.Comp.TileSize, (tile + 1) * grid.Comp.TileSize), Color.Aqua, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args.WorldHandle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@ using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Content.Shared.Atmos;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Pinpointer.UI;
|
||||
|
||||
@@ -30,7 +27,6 @@ public partial class NavMapControl : MapGridControl
|
||||
{
|
||||
[Dependency] private IResourceCache _cache = default!;
|
||||
private readonly SharedTransformSystem _transformSystem;
|
||||
private readonly SharedNavMapSystem _navMapSystem;
|
||||
|
||||
public EntityUid? Owner;
|
||||
public EntityUid? MapUid;
|
||||
@@ -44,10 +40,7 @@ public partial class NavMapControl : MapGridControl
|
||||
// Tracked data
|
||||
public Dictionary<EntityCoordinates, (bool Visible, Color Color)> TrackedCoordinates = new();
|
||||
public Dictionary<NetEntity, NavMapBlip> TrackedEntities = new();
|
||||
|
||||
public List<(Vector2, Vector2)> TileLines = new();
|
||||
public List<(Vector2, Vector2)> TileRects = new();
|
||||
public List<(Vector2[], Color)> TilePolygons = new();
|
||||
public Dictionary<Vector2i, List<NavMapLine>>? TileGrid = default!;
|
||||
|
||||
// Default colors
|
||||
public Color WallColor = new(102, 217, 102);
|
||||
@@ -60,23 +53,14 @@ public partial class NavMapControl : MapGridControl
|
||||
protected static float MinDisplayedRange = 8f;
|
||||
protected static float MaxDisplayedRange = 128f;
|
||||
protected static float DefaultDisplayedRange = 48f;
|
||||
protected float MinmapScaleModifier = 0.075f;
|
||||
protected float FullWallInstep = 0.165f;
|
||||
protected float ThinWallThickness = 0.165f;
|
||||
protected float ThinDoorThickness = 0.30f;
|
||||
|
||||
// Local variables
|
||||
private float _updateTimer = 1.0f;
|
||||
private float _updateTimer = 0.25f;
|
||||
private Dictionary<Color, Color> _sRGBLookUp = new();
|
||||
protected Color BackgroundColor;
|
||||
protected float BackgroundOpacity = 0.9f;
|
||||
private int _targetFontsize = 8;
|
||||
|
||||
private Dictionary<Vector2i, Vector2i> _horizLines = new();
|
||||
private Dictionary<Vector2i, Vector2i> _horizLinesReversed = new();
|
||||
private Dictionary<Vector2i, Vector2i> _vertLines = new();
|
||||
private Dictionary<Vector2i, Vector2i> _vertLinesReversed = new();
|
||||
|
||||
// Components
|
||||
private NavMapComponent? _navMap;
|
||||
private MapGridComponent? _grid;
|
||||
@@ -88,7 +72,6 @@ public partial class NavMapControl : MapGridControl
|
||||
private readonly Label _zoom = new()
|
||||
{
|
||||
VerticalAlignment = VAlignment.Top,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(8f, 8f),
|
||||
};
|
||||
|
||||
@@ -97,7 +80,6 @@ public partial class NavMapControl : MapGridControl
|
||||
Text = Loc.GetString("navmap-recenter"),
|
||||
VerticalAlignment = VAlignment.Top,
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(8f, 4f),
|
||||
Disabled = true,
|
||||
};
|
||||
@@ -105,10 +87,9 @@ public partial class NavMapControl : MapGridControl
|
||||
private readonly CheckBox _beacons = new()
|
||||
{
|
||||
Text = Loc.GetString("navmap-toggle-beacons"),
|
||||
Margin = new Thickness(4f, 0f),
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(4f, 0f),
|
||||
Pressed = true,
|
||||
};
|
||||
|
||||
@@ -117,8 +98,6 @@ public partial class NavMapControl : MapGridControl
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_transformSystem = EntManager.System<SharedTransformSystem>();
|
||||
_navMapSystem = EntManager.System<SharedNavMapSystem>();
|
||||
|
||||
BackgroundColor = Color.FromSrgb(TileColor.WithAlpha(BackgroundOpacity));
|
||||
|
||||
RectClipContent = true;
|
||||
@@ -133,8 +112,6 @@ public partial class NavMapControl : MapGridControl
|
||||
BorderColor = StyleNano.PanelDark
|
||||
},
|
||||
VerticalExpand = false,
|
||||
HorizontalExpand = true,
|
||||
SetWidth = 650f,
|
||||
Children =
|
||||
{
|
||||
new BoxContainer()
|
||||
@@ -153,7 +130,6 @@ public partial class NavMapControl : MapGridControl
|
||||
var topContainer = new BoxContainer()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
topPanel,
|
||||
@@ -181,9 +157,6 @@ public partial class NavMapControl : MapGridControl
|
||||
{
|
||||
EntManager.TryGetComponent(MapUid, out _navMap);
|
||||
EntManager.TryGetComponent(MapUid, out _grid);
|
||||
EntManager.TryGetComponent(MapUid, out _xform);
|
||||
EntManager.TryGetComponent(MapUid, out _physics);
|
||||
EntManager.TryGetComponent(MapUid, out _fixtures);
|
||||
|
||||
UpdateNavMap();
|
||||
}
|
||||
@@ -278,93 +251,119 @@ public partial class NavMapControl : MapGridControl
|
||||
EntManager.TryGetComponent(MapUid, out _physics);
|
||||
EntManager.TryGetComponent(MapUid, out _fixtures);
|
||||
|
||||
if (_navMap == null || _grid == null || _xform == null)
|
||||
return;
|
||||
|
||||
// Map re-centering
|
||||
_recenter.Disabled = DrawRecenter();
|
||||
|
||||
// Update zoom text
|
||||
_zoom.Text = Loc.GetString("navmap-zoom", ("value", $"{(DefaultDisplayedRange / WorldRange):0.0}"));
|
||||
_zoom.Text = Loc.GetString("navmap-zoom", ("value", $"{(DefaultDisplayedRange / WorldRange ):0.0}"));
|
||||
|
||||
if (_navMap == null || _xform == null)
|
||||
return;
|
||||
|
||||
// Update offset with physics local center
|
||||
var offset = Offset;
|
||||
|
||||
if (_physics != null)
|
||||
offset += _physics.LocalCenter;
|
||||
|
||||
var offsetVec = new Vector2(offset.X, -offset.Y);
|
||||
|
||||
// Wall sRGB
|
||||
if (!_sRGBLookUp.TryGetValue(WallColor, out var wallsRGB))
|
||||
{
|
||||
wallsRGB = Color.ToSrgb(WallColor);
|
||||
_sRGBLookUp[WallColor] = wallsRGB;
|
||||
}
|
||||
|
||||
// Draw floor tiles
|
||||
if (TilePolygons.Any())
|
||||
// Draw tiles
|
||||
if (_fixtures != null)
|
||||
{
|
||||
Span<Vector2> verts = new Vector2[8];
|
||||
|
||||
foreach (var (polygonVerts, polygonColor) in TilePolygons)
|
||||
foreach (var fixture in _fixtures.Fixtures.Values)
|
||||
{
|
||||
for (var i = 0; i < polygonVerts.Length; i++)
|
||||
if (fixture.Shape is not PolygonShape poly)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < poly.VertexCount; i++)
|
||||
{
|
||||
var vert = polygonVerts[i] - offset;
|
||||
var vert = poly.Vertices[i] - offset;
|
||||
|
||||
verts[i] = ScalePosition(new Vector2(vert.X, -vert.Y));
|
||||
}
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts[..polygonVerts.Length], polygonColor);
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts[..poly.VertexCount], TileColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw map lines
|
||||
if (TileLines.Any())
|
||||
var area = new Box2(-WorldRange, -WorldRange, WorldRange + 1f, WorldRange + 1f).Translated(offset);
|
||||
|
||||
// Drawing lines can be rather expensive due to the number of neighbors that need to be checked in order
|
||||
// to figure out where they should be drawn. However, we don't *need* to do check these every frame.
|
||||
// Instead, lets periodically update where to draw each line and then store these points in a list.
|
||||
// Then we can just run through the list each frame and draw the lines without any extra computation.
|
||||
|
||||
// Draw walls
|
||||
if (TileGrid != null && TileGrid.Count > 0)
|
||||
{
|
||||
var lines = new ValueList<Vector2>(TileLines.Count * 2);
|
||||
var walls = new ValueList<Vector2>();
|
||||
|
||||
foreach (var (o, t) in TileLines)
|
||||
foreach ((var chunk, var chunkedLines) in TileGrid)
|
||||
{
|
||||
var origin = ScalePosition(o - offsetVec);
|
||||
var terminus = ScalePosition(t - offsetVec);
|
||||
var offsetChunk = new Vector2(chunk.X, chunk.Y) * SharedNavMapSystem.ChunkSize;
|
||||
|
||||
lines.Add(origin);
|
||||
lines.Add(terminus);
|
||||
if (offsetChunk.X < area.Left - SharedNavMapSystem.ChunkSize || offsetChunk.X > area.Right)
|
||||
continue;
|
||||
|
||||
if (offsetChunk.Y < area.Bottom - SharedNavMapSystem.ChunkSize || offsetChunk.Y > area.Top)
|
||||
continue;
|
||||
|
||||
foreach (var chunkedLine in chunkedLines)
|
||||
{
|
||||
var start = ScalePosition(chunkedLine.Origin - new Vector2(offset.X, -offset.Y));
|
||||
var end = ScalePosition(chunkedLine.Terminus - new Vector2(offset.X, -offset.Y));
|
||||
|
||||
walls.Add(start);
|
||||
walls.Add(end);
|
||||
}
|
||||
}
|
||||
|
||||
if (lines.Count > 0)
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, lines.Span, wallsRGB);
|
||||
if (walls.Count > 0)
|
||||
{
|
||||
if (!_sRGBLookUp.TryGetValue(WallColor, out var sRGB))
|
||||
{
|
||||
sRGB = Color.ToSrgb(WallColor);
|
||||
_sRGBLookUp[WallColor] = sRGB;
|
||||
}
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, walls.Span, sRGB);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw map rects
|
||||
if (TileRects.Any())
|
||||
var airlockBuffer = Vector2.One * (MinimapScale / 2.25f) * 0.75f;
|
||||
var airlockLines = new ValueList<Vector2>();
|
||||
var foobarVec = new Vector2(1, -1);
|
||||
|
||||
foreach (var airlock in _navMap.Airlocks)
|
||||
{
|
||||
var rects = new ValueList<Vector2>(TileRects.Count * 8);
|
||||
var position = airlock.Position - offset;
|
||||
position = ScalePosition(position with { Y = -position.Y });
|
||||
airlockLines.Add(position + airlockBuffer);
|
||||
airlockLines.Add(position - airlockBuffer * foobarVec);
|
||||
|
||||
foreach (var (lt, rb) in TileRects)
|
||||
{
|
||||
var leftTop = ScalePosition(lt - offsetVec);
|
||||
var rightBottom = ScalePosition(rb - offsetVec);
|
||||
airlockLines.Add(position + airlockBuffer);
|
||||
airlockLines.Add(position + airlockBuffer * foobarVec);
|
||||
|
||||
var rightTop = new Vector2(rightBottom.X, leftTop.Y);
|
||||
var leftBottom = new Vector2(leftTop.X, rightBottom.Y);
|
||||
airlockLines.Add(position - airlockBuffer);
|
||||
airlockLines.Add(position + airlockBuffer * foobarVec);
|
||||
|
||||
rects.Add(leftTop);
|
||||
rects.Add(rightTop);
|
||||
rects.Add(rightTop);
|
||||
rects.Add(rightBottom);
|
||||
rects.Add(rightBottom);
|
||||
rects.Add(leftBottom);
|
||||
rects.Add(leftBottom);
|
||||
rects.Add(leftTop);
|
||||
}
|
||||
airlockLines.Add(position - airlockBuffer);
|
||||
airlockLines.Add(position - airlockBuffer * foobarVec);
|
||||
|
||||
if (rects.Count > 0)
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, rects.Span, wallsRGB);
|
||||
airlockLines.Add(position + airlockBuffer * -Vector2.UnitY);
|
||||
airlockLines.Add(position - airlockBuffer * -Vector2.UnitY);
|
||||
}
|
||||
|
||||
if (airlockLines.Count > 0)
|
||||
{
|
||||
if (!_sRGBLookUp.TryGetValue(WallColor, out var sRGB))
|
||||
{
|
||||
sRGB = Color.ToSrgb(WallColor);
|
||||
_sRGBLookUp[WallColor] = sRGB;
|
||||
}
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, airlockLines.Span, sRGB);
|
||||
}
|
||||
|
||||
// Invoke post wall drawing action
|
||||
if (PostWallDrawingAction != null)
|
||||
PostWallDrawingAction.Invoke(handle);
|
||||
|
||||
@@ -374,10 +373,10 @@ public partial class NavMapControl : MapGridControl
|
||||
var rectBuffer = new Vector2(5f, 3f);
|
||||
|
||||
// Calculate font size for current zoom level
|
||||
var fontSize = (int) Math.Round(1 / WorldRange * DefaultDisplayedRange * UIScale * _targetFontsize, 0);
|
||||
var fontSize = (int) Math.Round(1 / WorldRange * DefaultDisplayedRange * UIScale * _targetFontsize , 0);
|
||||
var font = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), fontSize);
|
||||
|
||||
foreach (var beacon in _navMap.Beacons.Values)
|
||||
foreach (var beacon in _navMap.Beacons)
|
||||
{
|
||||
var position = beacon.Position - offset;
|
||||
position = ScalePosition(position with { Y = -position.Y });
|
||||
@@ -410,6 +409,8 @@ public partial class NavMapControl : MapGridControl
|
||||
}
|
||||
|
||||
// Tracked entities (can use a supplied sprite as a marker instead; should probably just replace TrackedCoordinates with this eventually)
|
||||
var iconVertexUVs = new Dictionary<(Texture, Color), ValueList<DrawVertexUV2D>>();
|
||||
|
||||
foreach (var blip in TrackedEntities.Values)
|
||||
{
|
||||
if (blip.Blinks && !lit)
|
||||
@@ -418,6 +419,9 @@ public partial class NavMapControl : MapGridControl
|
||||
if (blip.Texture == null)
|
||||
continue;
|
||||
|
||||
if (!iconVertexUVs.TryGetValue((blip.Texture, blip.Color), out var vertexUVs))
|
||||
vertexUVs = new();
|
||||
|
||||
var mapPos = blip.Coordinates.ToMap(EntManager, _transformSystem);
|
||||
|
||||
if (mapPos.MapId != MapId.Nullspace)
|
||||
@@ -425,11 +429,29 @@ public partial class NavMapControl : MapGridControl
|
||||
var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
|
||||
position = ScalePosition(new Vector2(position.X, -position.Y));
|
||||
|
||||
var scalingCoefficient = MinmapScaleModifier * float.Sqrt(MinimapScale);
|
||||
var positionOffset = new Vector2(scalingCoefficient * blip.Texture.Width, scalingCoefficient * blip.Texture.Height);
|
||||
var scalingCoefficient = 2.5f;
|
||||
var positionOffset = scalingCoefficient * float.Sqrt(MinimapScale);
|
||||
|
||||
handle.DrawTextureRect(blip.Texture, new UIBox2(position - positionOffset, position + positionOffset), blip.Color);
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X - positionOffset, position.Y - positionOffset), new Vector2(1f, 1f)));
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X - positionOffset, position.Y + positionOffset), new Vector2(1f, 0f)));
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X + positionOffset, position.Y - positionOffset), new Vector2(0f, 1f)));
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X - positionOffset, position.Y + positionOffset), new Vector2(1f, 0f)));
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X + positionOffset, position.Y - positionOffset), new Vector2(0f, 1f)));
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X + positionOffset, position.Y + positionOffset), new Vector2(0f, 0f)));
|
||||
}
|
||||
|
||||
iconVertexUVs[(blip.Texture, blip.Color)] = vertexUVs;
|
||||
}
|
||||
|
||||
foreach ((var (texture, color), var vertexUVs) in iconVertexUVs)
|
||||
{
|
||||
if (!_sRGBLookUp.TryGetValue(color, out var sRGB))
|
||||
{
|
||||
sRGB = Color.ToSrgb(color);
|
||||
_sRGBLookUp[color] = sRGB;
|
||||
}
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, texture, vertexUVs.Span, sRGB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,266 +469,124 @@ public partial class NavMapControl : MapGridControl
|
||||
}
|
||||
|
||||
protected virtual void UpdateNavMap()
|
||||
{
|
||||
// Clear stale values
|
||||
TilePolygons.Clear();
|
||||
TileLines.Clear();
|
||||
TileRects.Clear();
|
||||
|
||||
UpdateNavMapFloorTiles();
|
||||
UpdateNavMapWallLines();
|
||||
UpdateNavMapAirlocks();
|
||||
}
|
||||
|
||||
private void UpdateNavMapFloorTiles()
|
||||
{
|
||||
if (_fixtures == null)
|
||||
return;
|
||||
|
||||
var verts = new Vector2[8];
|
||||
|
||||
foreach (var fixture in _fixtures.Fixtures.Values)
|
||||
{
|
||||
if (fixture.Shape is not PolygonShape poly)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < poly.VertexCount; i++)
|
||||
{
|
||||
var vert = poly.Vertices[i];
|
||||
verts[i] = new Vector2(MathF.Round(vert.X), MathF.Round(vert.Y));
|
||||
}
|
||||
|
||||
TilePolygons.Add((verts[..poly.VertexCount], TileColor));
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateNavMapWallLines()
|
||||
{
|
||||
if (_navMap == null || _grid == null)
|
||||
return;
|
||||
|
||||
// We'll use the following dictionaries to combine collinear wall lines
|
||||
_horizLines.Clear();
|
||||
_horizLinesReversed.Clear();
|
||||
_vertLines.Clear();
|
||||
_vertLinesReversed.Clear();
|
||||
TileGrid = GetDecodedWallChunks(_navMap.Chunks, _grid);
|
||||
}
|
||||
|
||||
const int southMask = (int) AtmosDirection.South << (int) NavMapChunkType.Wall;
|
||||
const int eastMask = (int) AtmosDirection.East << (int) NavMapChunkType.Wall;
|
||||
const int westMask = (int) AtmosDirection.West << (int) NavMapChunkType.Wall;
|
||||
const int northMask = (int) AtmosDirection.North << (int) NavMapChunkType.Wall;
|
||||
public Dictionary<Vector2i, List<NavMapLine>> GetDecodedWallChunks
|
||||
(Dictionary<Vector2i, NavMapChunk> chunks,
|
||||
MapGridComponent grid)
|
||||
{
|
||||
var decodedOutput = new Dictionary<Vector2i, List<NavMapLine>>();
|
||||
|
||||
foreach (var (chunkOrigin, chunk) in _navMap.Chunks)
|
||||
foreach ((var chunkOrigin, var chunk) in chunks)
|
||||
{
|
||||
for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
|
||||
var list = new List<NavMapLine>();
|
||||
|
||||
// TODO: Okay maybe I should just use ushorts lmao...
|
||||
for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
|
||||
{
|
||||
var tileData = chunk.TileData[i] & SharedNavMapSystem.WallMask;
|
||||
if (tileData == 0)
|
||||
var value = (int) Math.Pow(2, i);
|
||||
|
||||
var mask = chunk.TileData & value;
|
||||
|
||||
if (mask == 0x0)
|
||||
continue;
|
||||
|
||||
tileData >>= (int) NavMapChunkType.Wall;
|
||||
|
||||
var relativeTile = SharedNavMapSystem.GetTileFromIndex(i);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
|
||||
|
||||
if (tileData != SharedNavMapSystem.AllDirMask)
|
||||
{
|
||||
AddRectForThinWall(tileData, tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
tile = tile with { Y = -tile.Y };
|
||||
// Alright now we'll work out our edges
|
||||
var relativeTile = SharedNavMapSystem.GetTile(mask);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * grid.TileSize;
|
||||
var position = new Vector2(tile.X, -tile.Y);
|
||||
NavMapChunk? neighborChunk;
|
||||
bool neighbor;
|
||||
|
||||
// North edge
|
||||
var neighborData = 0;
|
||||
if (relativeTile.Y != SharedNavMapSystem.ChunkSize - 1)
|
||||
neighborData = chunk.TileData[i+1];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Up, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i + 1 - SharedNavMapSystem.ChunkSize];
|
||||
|
||||
if ((neighborData & southMask) == 0)
|
||||
if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize),
|
||||
tile + new Vector2i(_grid.TileSize, -_grid.TileSize), _horizLines,
|
||||
_horizLinesReversed);
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, 1), out neighborChunk) &&
|
||||
(neighborChunk.TileData &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
|
||||
neighbor = (chunk.TileData & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (!neighbor)
|
||||
{
|
||||
// Add points
|
||||
list.Add(new NavMapLine(position + new Vector2(0f, -grid.TileSize), position + new Vector2(grid.TileSize, -grid.TileSize)));
|
||||
}
|
||||
|
||||
// East edge
|
||||
neighborData = 0;
|
||||
if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1)
|
||||
neighborData = chunk.TileData[i+SharedNavMapSystem.ChunkSize];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize];
|
||||
|
||||
if ((neighborData & westMask) == 0)
|
||||
if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize),
|
||||
tile + new Vector2i(_grid.TileSize, 0), _vertLines, _vertLinesReversed);
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(1, 0), out neighborChunk) &&
|
||||
(neighborChunk.TileData &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
|
||||
neighbor = (chunk.TileData & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (!neighbor)
|
||||
{
|
||||
// Add points
|
||||
list.Add(new NavMapLine(position + new Vector2(grid.TileSize, -grid.TileSize), position + new Vector2(grid.TileSize, 0f)));
|
||||
}
|
||||
|
||||
// South edge
|
||||
neighborData = 0;
|
||||
if (relativeTile.Y != 0)
|
||||
neighborData = chunk.TileData[i-1];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize];
|
||||
|
||||
if ((neighborData & northMask) == 0)
|
||||
if (relativeTile.Y == 0)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), _horizLines,
|
||||
_horizLinesReversed);
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, -1), out neighborChunk) &&
|
||||
(neighborChunk.TileData &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, SharedNavMapSystem.ChunkSize - 1))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, -1));
|
||||
neighbor = (chunk.TileData & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (!neighbor)
|
||||
{
|
||||
// Add points
|
||||
list.Add(new NavMapLine(position + new Vector2(grid.TileSize, 0f), position));
|
||||
}
|
||||
|
||||
// West edge
|
||||
neighborData = 0;
|
||||
if (relativeTile.X != 0)
|
||||
neighborData = chunk.TileData[i-SharedNavMapSystem.ChunkSize];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize];
|
||||
|
||||
if ((neighborData & eastMask) == 0)
|
||||
if (relativeTile.X == 0)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, _vertLines,
|
||||
_vertLinesReversed);
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(-1, 0), out neighborChunk) &&
|
||||
(neighborChunk.TileData &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(SharedNavMapSystem.ChunkSize - 1, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(-1, 0));
|
||||
neighbor = (chunk.TileData & flag) != 0x0;
|
||||
}
|
||||
|
||||
// Add a diagonal line for interiors. Unless there are a lot of double walls, there is no point combining these
|
||||
TileLines.Add((tile + new Vector2(0, -_grid.TileSize), tile + new Vector2(_grid.TileSize, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
// Record the combined lines
|
||||
foreach (var (origin, terminal) in _horizLines)
|
||||
{
|
||||
TileLines.Add((origin, terminal));
|
||||
}
|
||||
|
||||
foreach (var (origin, terminal) in _vertLines)
|
||||
{
|
||||
TileLines.Add((origin, terminal));
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateNavMapAirlocks()
|
||||
{
|
||||
if (_navMap == null || _grid == null)
|
||||
return;
|
||||
|
||||
foreach (var chunk in _navMap.Chunks.Values)
|
||||
{
|
||||
for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
|
||||
{
|
||||
var tileData = chunk.TileData[i] & SharedNavMapSystem.AirlockMask;
|
||||
if (tileData == 0)
|
||||
continue;
|
||||
|
||||
tileData >>= (int) NavMapChunkType.Airlock;
|
||||
|
||||
var relative = SharedNavMapSystem.GetTileFromIndex(i);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relative) * _grid.TileSize;
|
||||
|
||||
// If the edges of an airlock tile are not all occupied, draw a thin airlock for each edge
|
||||
if (tileData != SharedNavMapSystem.AllDirMask)
|
||||
if (!neighbor)
|
||||
{
|
||||
AddRectForThinAirlock(tileData, tile);
|
||||
continue;
|
||||
// Add point
|
||||
list.Add(new NavMapLine(position, position + new Vector2(0f, -grid.TileSize)));
|
||||
}
|
||||
|
||||
// Otherwise add a single full tile airlock
|
||||
TileRects.Add((new Vector2(tile.X + FullWallInstep, -tile.Y - FullWallInstep),
|
||||
new Vector2(tile.X - FullWallInstep + 1f, -tile.Y + FullWallInstep - 1)));
|
||||
|
||||
TileLines.Add((new Vector2(tile.X + 0.5f, -tile.Y - FullWallInstep),
|
||||
new Vector2(tile.X + 0.5f, -tile.Y + FullWallInstep - 1)));
|
||||
// Draw a diagonal line for interiors.
|
||||
list.Add(new NavMapLine(position + new Vector2(0f, -grid.TileSize), position + new Vector2(grid.TileSize, 0f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRectForThinWall(int tileData, Vector2i tile)
|
||||
{
|
||||
var leftTop = new Vector2(-0.5f, 0.5f - ThinWallThickness);
|
||||
var rightBottom = new Vector2(0.5f, 0.5f);
|
||||
|
||||
for (var i = 0; i < SharedNavMapSystem.Directions; i++)
|
||||
{
|
||||
var dirMask = 1 << i;
|
||||
if ((tileData & dirMask) == 0)
|
||||
continue;
|
||||
|
||||
var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
|
||||
|
||||
// TODO NAVMAP
|
||||
// Consider using faster rotation operations, given that these are always 90 degree increments
|
||||
var angle = -((AtmosDirection) dirMask).ToAngle();
|
||||
TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition));
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRectForThinAirlock(int tileData, Vector2i tile)
|
||||
{
|
||||
var leftTop = new Vector2(-0.5f + FullWallInstep, 0.5f - FullWallInstep - ThinDoorThickness);
|
||||
var rightBottom = new Vector2(0.5f - FullWallInstep, 0.5f - FullWallInstep);
|
||||
var centreTop = new Vector2(0f, 0.5f - FullWallInstep - ThinDoorThickness);
|
||||
var centreBottom = new Vector2(0f, 0.5f - FullWallInstep);
|
||||
|
||||
for (var i = 0; i < SharedNavMapSystem.Directions; i++)
|
||||
{
|
||||
var dirMask = 1 << i;
|
||||
if ((tileData & dirMask) == 0)
|
||||
continue;
|
||||
|
||||
var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
|
||||
var angle = -((AtmosDirection) dirMask).ToAngle();
|
||||
TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition));
|
||||
TileLines.Add((angle.RotateVec(centreTop) + tilePosition, angle.RotateVec(centreBottom) + tilePosition));
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddOrUpdateNavMapLine(
|
||||
Vector2i origin,
|
||||
Vector2i terminus,
|
||||
Dictionary<Vector2i, Vector2i> lookup,
|
||||
Dictionary<Vector2i, Vector2i> lookupReversed)
|
||||
{
|
||||
Vector2i foundTermius;
|
||||
Vector2i foundOrigin;
|
||||
|
||||
// Does our new line end at the beginning of an existing line?
|
||||
if (lookup.Remove(terminus, out foundTermius))
|
||||
{
|
||||
DebugTools.Assert(lookupReversed[foundTermius] == terminus);
|
||||
|
||||
// Does our new line start at the end of an existing line?
|
||||
if (lookupReversed.Remove(origin, out foundOrigin))
|
||||
{
|
||||
// Our new line just connects two existing lines
|
||||
DebugTools.Assert(lookup[foundOrigin] == origin);
|
||||
lookup[foundOrigin] = foundTermius;
|
||||
lookupReversed[foundTermius] = foundOrigin;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Our new line precedes an existing line, extending it further to the left
|
||||
lookup[origin] = foundTermius;
|
||||
lookupReversed[foundTermius] = origin;
|
||||
}
|
||||
return;
|
||||
decodedOutput.Add(chunkOrigin, list);
|
||||
}
|
||||
|
||||
// Does our new line start at the end of an existing line?
|
||||
if (lookupReversed.Remove(origin, out foundOrigin))
|
||||
{
|
||||
// Our new line just extends an existing line further to the right
|
||||
DebugTools.Assert(lookup[foundOrigin] == origin);
|
||||
lookup[foundOrigin] = terminus;
|
||||
lookupReversed[terminus] = foundOrigin;
|
||||
return;
|
||||
}
|
||||
|
||||
// Completely disconnected line segment.
|
||||
lookup.Add(origin, terminus);
|
||||
lookupReversed.Add(terminus, origin);
|
||||
return decodedOutput;
|
||||
}
|
||||
|
||||
protected Vector2 GetOffset()
|
||||
@@ -732,3 +612,15 @@ public struct NavMapBlip
|
||||
Selectable = selectable;
|
||||
}
|
||||
}
|
||||
|
||||
public struct NavMapLine
|
||||
{
|
||||
public readonly Vector2 Origin;
|
||||
public readonly Vector2 Terminus;
|
||||
|
||||
public NavMapLine(Vector2 origin, Vector2 terminus)
|
||||
{
|
||||
Origin = origin;
|
||||
Terminus = terminus;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,12 @@ using Robust.Client;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Players.PlayTimeTracking;
|
||||
|
||||
public sealed class JobRequirementsManager : ISharedPlaytimeManager
|
||||
public sealed class JobRequirementsManager
|
||||
{
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IClientNetManager _net = default!;
|
||||
@@ -134,13 +133,5 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, TimeSpan> GetPlayTimes(ICommonSession session)
|
||||
{
|
||||
if (session != _playerManager.LocalSession)
|
||||
{
|
||||
return new Dictionary<string, TimeSpan>();
|
||||
}
|
||||
|
||||
return _roles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Polymorph.Components;
|
||||
using Content.Shared.Polymorph.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Polymorph.Systems;
|
||||
|
||||
public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
private EntityQuery<AppearanceComponent> _appearanceQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_appearanceQuery = GetEntityQuery<AppearanceComponent>();
|
||||
|
||||
SubscribeLocalEvent<ChameleonDisguiseComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<ChameleonDisguiseComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
CopyComp<SpriteComponent>(ent);
|
||||
CopyComp<GenericVisualizerComponent>(ent);
|
||||
CopyComp<SolutionContainerVisualsComponent>(ent);
|
||||
|
||||
// reload appearance to hopefully prevent any invisible layers
|
||||
if (_appearanceQuery.TryComp(ent, out var appearance))
|
||||
_appearance.QueueUpdate(ent, appearance);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Content.Shared.Popups;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
@@ -162,15 +163,6 @@ namespace Content.Client.Popups
|
||||
PopupEntity(message, uid, type);
|
||||
}
|
||||
|
||||
public override void PopupClient(string? message, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (recipient == null)
|
||||
return;
|
||||
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
PopupCursor(message, recipient.Value, type);
|
||||
}
|
||||
|
||||
public override void PopupClient(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (recipient == null)
|
||||
@@ -180,15 +172,6 @@ namespace Content.Client.Popups
|
||||
PopupEntity(message, uid, recipient.Value, type);
|
||||
}
|
||||
|
||||
public override void PopupClient(string? message, EntityCoordinates coordinates, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (recipient == null)
|
||||
return;
|
||||
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
PopupCoordinates(message, coordinates, recipient.Value, type);
|
||||
}
|
||||
|
||||
public override void PopupEntity(string? message, EntityUid uid, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (TryComp(uid, out TransformComponent? transform))
|
||||
@@ -201,12 +184,6 @@ namespace Content.Client.Popups
|
||||
PopupEntity(message, uid, recipient.Value, type);
|
||||
}
|
||||
|
||||
public override void PopupPredicted(string? recipientMessage, string? othersMessage, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (recipient != null && _timing.IsFirstTimePredicted)
|
||||
PopupEntity(recipientMessage, uid, recipient.Value, type);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Network Event Handlers
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Wires;
|
||||
|
||||
namespace Content.Client.Power;
|
||||
|
||||
public sealed class ActivatableUIRequiresPowerSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ActivatableUIRequiresPowerComponent, ActivatableUIOpenAttemptEvent>(OnActivate);
|
||||
}
|
||||
|
||||
private void OnActivate(EntityUid uid, ActivatableUIRequiresPowerComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
// Client can't predict the power properly at the moment so rely upon the server to do it.
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user