Merge remote-tracking branch 'upstream/master' into fucking-upstream
This commit is contained in:
@@ -12,6 +12,7 @@ tab_width = 4
|
||||
#end_of_line = crlf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 120
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
|
||||
|
||||
@@ -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" };
|
||||
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" };
|
||||
|
||||
[ParamsSource(nameof(MapsSource))]
|
||||
public string Map;
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
|
||||
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 newJobIcon)
|
||||
public void OnJobIconChanged(string newJobIconId)
|
||||
{
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIcon));
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,7 +57,7 @@ namespace Content.Client.Access.UI
|
||||
|
||||
_window.SetCurrentName(cast.CurrentName);
|
||||
_window.SetCurrentJob(cast.CurrentJob);
|
||||
_window.SetAllowedIcons(cast.Icons);
|
||||
_window.SetAllowedIcons(cast.Icons, cast.CurrentJobIconId);
|
||||
}
|
||||
|
||||
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)
|
||||
public void SetAllowedIcons(HashSet<string> icons, string currentJobIconId)
|
||||
{
|
||||
IconGrid.DisposeAllChildren();
|
||||
|
||||
@@ -79,6 +79,10 @@ 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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +247,10 @@ namespace Content.Client.Actions
|
||||
if (action.ClientExclusive)
|
||||
{
|
||||
if (instantAction.Event != null)
|
||||
{
|
||||
instantAction.Event.Performer = user;
|
||||
instantAction.Event.Action = actionId;
|
||||
}
|
||||
|
||||
PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[RegisterComponent]
|
||||
public sealed partial class HeadstandComponent : SharedHeadstandComponent
|
||||
{
|
||||
|
||||
|
||||
@@ -3,6 +3,5 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class KillSignComponent : SharedKillSignComponent
|
||||
{ }
|
||||
[RegisterComponent]
|
||||
public sealed partial class KillSignComponent : SharedKillSignComponent;
|
||||
|
||||
@@ -126,12 +126,15 @@ namespace Content.Client.Administration.Managers
|
||||
|
||||
public AdminData? GetAdminData(EntityUid uid, bool includeDeAdmin = false)
|
||||
{
|
||||
return uid == _player.LocalEntity ? _adminData : null;
|
||||
if (uid == _player.LocalEntity && (_adminData?.Active ?? includeDeAdmin))
|
||||
return _adminData;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public AdminData? GetAdminData(ICommonSession session, bool includeDeAdmin = false)
|
||||
{
|
||||
if (_player.LocalUser == session.UserId)
|
||||
if (_player.LocalUser == session.UserId && (_adminData?.Active ?? includeDeAdmin))
|
||||
return _adminData;
|
||||
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Administration;
|
||||
|
||||
namespace Content.Client.Administration.Systems;
|
||||
|
||||
public sealed class AdminFrozenSystem : SharedAdminFrozenSystem
|
||||
{
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
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;
|
||||
@@ -11,10 +14,12 @@ 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)
|
||||
@@ -33,6 +38,24 @@ 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,6 +3,7 @@ using Content.Shared.Explosion;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
@@ -22,7 +23,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private readonly SpawnExplosionEui _eui;
|
||||
private List<MapId> _mapData = new();
|
||||
@@ -37,6 +38,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_transform = _entMan.System<TransformSystem>();
|
||||
_eui = eui;
|
||||
|
||||
ExplosionOption.OnItemSelected += ExplosionSelected;
|
||||
@@ -104,7 +106,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
||||
|
||||
_pausePreview = true;
|
||||
MapOptions.Select(_mapData.IndexOf(transform.MapID));
|
||||
(MapX.Value, MapY.Value) = transform.MapPosition.Position;
|
||||
(MapX.Value, MapY.Value) = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: transform).Position;
|
||||
_pausePreview = false;
|
||||
|
||||
UpdatePreview();
|
||||
|
||||
17
Content.Client/Animations/TrackUserComponent.cs
Normal file
17
Content.Client/Animations/TrackUserComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
}
|
||||
@@ -9,7 +9,6 @@ namespace Content.Client.Audio.Jukebox;
|
||||
|
||||
public sealed class JukeboxBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
|
||||
@@ -11,6 +11,7 @@ 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()
|
||||
{
|
||||
@@ -37,11 +38,8 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
|
||||
|
||||
while (query.MoveNext(out var uid, out var jukebox, out var ui))
|
||||
{
|
||||
if (!ui.OpenInterfaces.TryGetValue(JukeboxUiKey.Key, out var baseBui) ||
|
||||
baseBui is not JukeboxBoundUserInterface bui)
|
||||
{
|
||||
if (!_uiSystem.TryGetOpenUi<JukeboxBoundUserInterface>((uid, ui), JukeboxUiKey.Key, out var bui))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (obj.Removed?.TryGetValue(typeof(JukeboxPrototype), out var removed) ?? false)
|
||||
{
|
||||
@@ -67,15 +65,9 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
|
||||
|
||||
private void OnJukeboxAfterState(Entity<JukeboxComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (!TryComp(ent, out UserInterfaceComponent? ui))
|
||||
if (!_uiSystem.TryGetOpenUi<JukeboxBoundUserInterface>(ent.Owner, JukeboxUiKey.Key, out var bui))
|
||||
return;
|
||||
|
||||
if (!ui.OpenInterfaces.TryGetValue(JukeboxUiKey.Key, out var baseBui) ||
|
||||
baseBui is not JukeboxBoundUserInterface bui)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bui.Reload();
|
||||
}
|
||||
|
||||
|
||||
@@ -87,14 +87,12 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
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";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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, InjectorComponent.SolutionName, out _, out var solution))
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
|
||||
return;
|
||||
|
||||
// only updates the UI if any of the details are different than they previously were
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client.Guidebook.Components;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using JetBrains.Annotations;
|
||||
@@ -34,6 +35,7 @@ namespace Content.Client.Chemistry.UI
|
||||
_window = new()
|
||||
{
|
||||
Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName,
|
||||
HelpGuidebookIds = EntMan.GetComponent<GuideHelpComponent>(Owner).Guides
|
||||
};
|
||||
|
||||
_window.OpenCentered();
|
||||
|
||||
59
Content.Client/Chemistry/UI/SolutionStatusControl.cs
Normal file
59
Content.Client/Chemistry/UI/SolutionStatusControl.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
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);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.GameObjects.SpriteComponent;
|
||||
@@ -53,6 +54,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
};
|
||||
|
||||
[Dependency] private readonly IResourceCache _cache = default!;
|
||||
[Dependency] private readonly ISerializationManager _serialization = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
@@ -296,6 +298,7 @@ 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)
|
||||
@@ -340,6 +343,28 @@ 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);
|
||||
|
||||
@@ -2,6 +2,4 @@ using Content.Shared.CriminalRecords.Systems;
|
||||
|
||||
namespace Content.Client.CriminalRecords.Systems;
|
||||
|
||||
public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleSystem
|
||||
{
|
||||
}
|
||||
public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleSystem;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.CriminalRecords.Systems;
|
||||
|
||||
namespace Content.Client.CriminalRecords.Systems;
|
||||
|
||||
public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSystem;
|
||||
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.CriminalRecords.Systems;
|
||||
|
||||
namespace Content.Client.CriminalRecords.Systems;
|
||||
|
||||
public sealed class CriminalRecordsSystem : SharedCriminalRecordsSystem;
|
||||
@@ -5,11 +5,13 @@ namespace Content.Client.Decals;
|
||||
|
||||
public sealed class ToggleDecalCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "toggledecals";
|
||||
public string Description => "Toggles decaloverlay";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DecalSystem>().ToggleOverlay();
|
||||
_e.System<DecalSystem>().ToggleOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Content.Client.Decals.UI;
|
||||
public sealed partial class DecalPlacerWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
private readonly DecalPlacementSystem _decalPlacementSystem;
|
||||
|
||||
@@ -39,7 +40,7 @@ public sealed partial class DecalPlacerWindow : DefaultWindow
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_decalPlacementSystem = EntitySystem.Get<DecalPlacementSystem>();
|
||||
_decalPlacementSystem = _e.System<DecalPlacementSystem>();
|
||||
|
||||
// This needs to be done in C# so we can have custom stuff passed in the constructor
|
||||
// and thus have a proper step size
|
||||
|
||||
8
Content.Client/DeviceNetwork/JammerSystem.cs
Normal file
8
Content.Client/DeviceNetwork/JammerSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Radio.EntitySystems;
|
||||
|
||||
namespace Content.Client.DeviceNetwork;
|
||||
|
||||
public sealed class JammerSystem : SharedJammerSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -153,7 +153,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
|
||||
}
|
||||
}
|
||||
else if (state == VisualState.OverlayCharging)
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, new RSI.StateId("disposal-charging"));
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, chargingState);
|
||||
else
|
||||
_animationSystem.Stop(uid, AnimationKey);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ public sealed class DoAfterOverlay : Overlay
|
||||
private readonly SharedContainerSystem _container;
|
||||
|
||||
private readonly Texture _barTexture;
|
||||
private readonly ShaderInstance _unshadedShader;
|
||||
|
||||
/// <summary>
|
||||
/// Flash time for cancelled DoAfters
|
||||
@@ -35,7 +36,7 @@ public sealed class DoAfterOverlay : Overlay
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
|
||||
public DoAfterOverlay(IEntityManager entManager, IGameTiming timing, IPlayerManager player)
|
||||
public DoAfterOverlay(IEntityManager entManager, IPrototypeManager protoManager, IGameTiming timing, IPlayerManager player)
|
||||
{
|
||||
_entManager = entManager;
|
||||
_timing = timing;
|
||||
@@ -46,6 +47,8 @@ public sealed class DoAfterOverlay : Overlay
|
||||
_progressColor = _entManager.System<ProgressColorSystem>();
|
||||
var sprite = new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Misc/progress_bar.rsi"), "icon");
|
||||
_barTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite);
|
||||
|
||||
_unshadedShader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
@@ -80,6 +83,13 @@ 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
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.DoAfter;
|
||||
|
||||
@@ -14,12 +15,13 @@ public sealed class DoAfterSystem : SharedDoAfterSystem
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_overlay.AddOverlay(new DoAfterOverlay(EntityManager, GameTiming, _player));
|
||||
_overlay.AddOverlay(new DoAfterOverlay(EntityManager, _prototype, GameTiming, _player));
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
|
||||
@@ -3,7 +3,5 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Extinguisher;
|
||||
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent
|
||||
{
|
||||
}
|
||||
[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.MapID)
|
||||
if (args.OldMapId != args.Transform.MapUid)
|
||||
component.LastRotation = GetRotation(uid, args.Transform);
|
||||
}
|
||||
|
||||
|
||||
48
Content.Client/Fax/System/FaxVisualsSystem.cs
Normal file
48
Content.Client/Fax/System/FaxVisualsSystem.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,8 +52,27 @@ public sealed class FaxBoundUi : BoundUserInterface
|
||||
}
|
||||
|
||||
using var reader = new StreamReader(file);
|
||||
|
||||
var firstLine = await reader.ReadLineAsync();
|
||||
string? label = null;
|
||||
var content = await reader.ReadToEndAsync();
|
||||
SendMessage(new FaxFileMessage(content[..Math.Min(content.Length, FaxFileMessageValidation.MaxContentSize)], _window.OfficePaper));
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
private void OnSendButtonPressed()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Client.GPS.Components;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -13,11 +14,13 @@ public sealed class HandheldGpsStatusControl : Control
|
||||
private readonly RichTextLabel _label;
|
||||
private float _updateDif;
|
||||
private readonly IEntityManager _entMan;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
|
||||
public HandheldGpsStatusControl(Entity<HandheldGPSComponent> parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_entMan = IoCManager.Resolve<IEntityManager>();
|
||||
_transform = _entMan.System<TransformSystem>();
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
AddChild(_label);
|
||||
UpdateGpsDetails();
|
||||
@@ -41,7 +44,7 @@ public sealed class HandheldGpsStatusControl : Control
|
||||
var posText = "Error";
|
||||
if (_entMan.TryGetComponent(_parent, out TransformComponent? transComp))
|
||||
{
|
||||
var pos = transComp.MapPosition;
|
||||
var pos = _transform.GetMapCoordinates(_parent.Owner, xform: transComp);
|
||||
var x = (int) pos.X;
|
||||
var y = (int) pos.Y;
|
||||
posText = $"({x}, {y})";
|
||||
|
||||
@@ -17,14 +17,6 @@ namespace Content.Client.GameTicking.Commands
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var ticker = _entitySystem.GetEntitySystem<ClientGameTicker>();
|
||||
var window = ticker._window;
|
||||
|
||||
if (!ticker.IsGameStarted && window != null)
|
||||
{
|
||||
window.OpenCentered();
|
||||
return;
|
||||
}
|
||||
shell.WriteLine("You can't open manifest right now");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.RoundEnd;
|
||||
@@ -6,7 +7,7 @@ using Content.Shared.GameWindow;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.State;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.GameTicking.Managers
|
||||
{
|
||||
@@ -14,17 +15,14 @@ namespace Content.Client.GameTicking.Managers
|
||||
public sealed class ClientGameTicker : SharedGameTicker
|
||||
{
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = 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!;
|
||||
|
||||
[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>
|
||||
public RoundEndSummaryWindow? _window;
|
||||
|
||||
[ViewVariables] public bool AreWeReady { get; private set; }
|
||||
[ViewVariables] public bool IsGameStarted { get; private set; }
|
||||
[ViewVariables] public string? RestartSound { get; private set; }
|
||||
@@ -44,8 +42,6 @@ namespace Content.Client.GameTicking.Managers
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
DebugTools.Assert(!_initialized);
|
||||
|
||||
SubscribeNetworkEvent<TickerJoinLobbyEvent>(JoinLobby);
|
||||
SubscribeNetworkEvent<TickerJoinGameEvent>(JoinGame);
|
||||
SubscribeNetworkEvent<TickerConnectionStatusEvent>(ConnectionStatus);
|
||||
@@ -53,14 +49,33 @@ namespace Content.Client.GameTicking.Managers
|
||||
SubscribeNetworkEvent<TickerLobbyInfoEvent>(LobbyInfo);
|
||||
SubscribeNetworkEvent<TickerLobbyCountdownEvent>(LobbyCountdown);
|
||||
SubscribeNetworkEvent<RoundEndMessageEvent>(RoundEnd);
|
||||
SubscribeNetworkEvent<RequestWindowAttentionEvent>(msg =>
|
||||
{
|
||||
IoCManager.Resolve<IClyde>().RequestWindowAttention();
|
||||
});
|
||||
SubscribeNetworkEvent<RequestWindowAttentionEvent>(OnAttentionRequest);
|
||||
SubscribeNetworkEvent<TickerLateJoinStatusEvent>(LateJoinStatus);
|
||||
SubscribeNetworkEvent<TickerJobsAvailableEvent>(UpdateJobsAvailable);
|
||||
|
||||
_initialized = true;
|
||||
_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();
|
||||
}
|
||||
|
||||
private void LateJoinStatus(TickerLateJoinStatusEvent message)
|
||||
@@ -132,12 +147,7 @@ namespace Content.Client.GameTicking.Managers
|
||||
// Force an update in the event of this song being the same as the last.
|
||||
RestartSound = message.RestartSound;
|
||||
|
||||
// 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);
|
||||
_userInterfaceManager.GetUIController<RoundEndSummaryUIController>().OpenRoundEndSummaryWindow(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ 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;
|
||||
@@ -128,7 +129,7 @@ public sealed partial class GuideReagentEmbed : BoxContainer, IDocumentTag, ISea
|
||||
|
||||
var groupLabel = new RichTextLabel();
|
||||
groupLabel.SetMarkup(Loc.GetString("guidebook-reagent-effects-metabolism-group-rate",
|
||||
("group", group), ("rate", effect.MetabolismRate)));
|
||||
("group", _prototype.Index<MetabolismGroupPrototype>(group).LocalizedName), ("rate", effect.MetabolismRate)));
|
||||
var descriptionLabel = new RichTextLabel
|
||||
{
|
||||
Margin = new Thickness(25, 0, 10, 0)
|
||||
|
||||
@@ -42,7 +42,7 @@ public class GuideEntry
|
||||
}
|
||||
|
||||
[Prototype("guideEntry")]
|
||||
public sealed class GuideEntryPrototype : GuideEntry, IPrototype
|
||||
public sealed partial class GuideEntryPrototype : GuideEntry, IPrototype
|
||||
{
|
||||
public string ID => Id;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,11 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
});
|
||||
}
|
||||
|
||||
public void OpenHelp(List<string> guides)
|
||||
{
|
||||
OnGuidebookOpen?.Invoke(guides, null, null, true, guides[0]);
|
||||
}
|
||||
|
||||
private void OnInteract(EntityUid uid, GuideHelpComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
|
||||
var groupTitleText = $"{Loc.GetString(
|
||||
"health-analyzer-window-damage-group-text",
|
||||
("damageGroup", Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId)),
|
||||
("damageGroup", _prototypes.Index<DamageGroupPrototype>(damageGroupId).LocalizedName),
|
||||
("amount", damageAmount)
|
||||
)}";
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
|
||||
var damageString = Loc.GetString(
|
||||
"health-analyzer-window-damage-type-text",
|
||||
("damageType", Loc.GetString("health-analyzer-window-damage-type-" + type)),
|
||||
("damageType", _prototypes.Index<DamageTypePrototype>(type).LocalizedName),
|
||||
("amount", typeAmount)
|
||||
);
|
||||
|
||||
|
||||
@@ -111,9 +111,12 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||
/// </remarks>
|
||||
public override void LoadProfile(
|
||||
EntityUid uid,
|
||||
HumanoidCharacterProfile profile,
|
||||
HumanoidCharacterProfile? profile,
|
||||
HumanoidAppearanceComponent? humanoid = null)
|
||||
{
|
||||
if (profile == null)
|
||||
return;
|
||||
|
||||
if (!Resolve(uid, ref humanoid))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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;
|
||||
@@ -17,7 +18,7 @@ public sealed class ImplanterStatusControl : Control
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
_label.MaxWidth = 350;
|
||||
AddChild(_label);
|
||||
AddChild(new ClipControl { Children = { _label } });
|
||||
|
||||
Update();
|
||||
}
|
||||
@@ -42,17 +43,12 @@ public sealed class ImplanterStatusControl : Control
|
||||
_ => Loc.GetString("injector-invalid-injector-toggle-mode")
|
||||
};
|
||||
|
||||
var (implantName, implantDescription) = _parent.ImplanterSlot.HasItem switch
|
||||
{
|
||||
false => (Loc.GetString("implanter-empty-text"), ""),
|
||||
true => (_parent.ImplantData.Item1, _parent.ImplantData.Item2),
|
||||
};
|
||||
|
||||
var implantName = _parent.ImplanterSlot.HasItem
|
||||
? _parent.ImplantData.Item1
|
||||
: Loc.GetString("implanter-empty-text");
|
||||
|
||||
_label.SetMarkup(Loc.GetString("implanter-label",
|
||||
("implantName", implantName),
|
||||
("implantDescription", implantDescription),
|
||||
("modeString", modeStringLocalized),
|
||||
("lineBreak", "\n")));
|
||||
("modeString", modeStringLocalized)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ 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);
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace Content.Client.Inventory
|
||||
|
||||
public void UIInventoryStorageActivate(string slot)
|
||||
{
|
||||
EntityManager.EntityNetManager?.SendSystemNetworkMessage(new OpenSlotStorageNetworkMessage(slot));
|
||||
EntityManager.RaisePredictiveEvent(new OpenSlotStorageNetworkMessage(slot));
|
||||
}
|
||||
|
||||
public void UIInventoryExamine(string slot, EntityUid uid)
|
||||
@@ -251,6 +251,7 @@ 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.SpriteView.SetEntity(null);
|
||||
button.SetEntity(null);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace Content.Client.Inventory
|
||||
else
|
||||
return;
|
||||
|
||||
button.SpriteView.SetEntity(viewEnt);
|
||||
button.SetEntity(viewEnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
Content.Client/Items/UI/PollingItemStatusControl.cs
Normal file
28
Content.Client/Items/UI/PollingItemStatusControl.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
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);
|
||||
}
|
||||
18
Content.Client/Labels/EntitySystems/HandLabelerSystem.cs
Normal file
18
Content.Client/Labels/EntitySystems/HandLabelerSystem.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
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,4 +1,5 @@
|
||||
using Content.Shared.Labels;
|
||||
using Content.Shared.Labels.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Labels.UI
|
||||
@@ -8,11 +9,14 @@ 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()
|
||||
@@ -27,24 +31,25 @@ namespace Content.Client.Labels.UI
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OnLabelChanged += OnLabelChanged;
|
||||
Reload();
|
||||
}
|
||||
|
||||
private void OnLabelChanged(string newLabel)
|
||||
{
|
||||
SendMessage(new HandLabelerLabelChangedMessage(newLabel));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the UI state based on server-sent info
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (_window == null || state is not HandLabelerBoundUserInterfaceState cast)
|
||||
// Focus moment
|
||||
if (_entManager.TryGetComponent(Owner, out HandLabelerComponent? labeler) &&
|
||||
labeler.AssignedLabel.Equals(newLabel))
|
||||
return;
|
||||
|
||||
_window.SetCurrentLabel(cast.CurrentLabel);
|
||||
SendPredictedMessage(new HandLabelerLabelChangedMessage(newLabel));
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
if (_window == null || !_entManager.TryGetComponent(Owner, out HandLabelerComponent? component))
|
||||
return;
|
||||
|
||||
_window.SetCurrentLabel(component.AssignedLabel);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
@@ -9,16 +9,39 @@ 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 => OnLabelChanged?.Invoke(e.Text);
|
||||
LabelLineEdit.OnFocusExit += e => OnLabelChanged?.Invoke(e.Text);
|
||||
LabelLineEdit.OnTextEntered += e =>
|
||||
{
|
||||
_label = e.Text;
|
||||
OnLabelChanged?.Invoke(_label);
|
||||
};
|
||||
|
||||
LabelLineEdit.OnFocusEnter += _ => _focused = true;
|
||||
LabelLineEdit.OnFocusExit += _ =>
|
||||
{
|
||||
_focused = false;
|
||||
LabelLineEdit.Text = _label;
|
||||
};
|
||||
}
|
||||
|
||||
public void SetCurrentLabel(string label)
|
||||
{
|
||||
if (label == _label)
|
||||
return;
|
||||
|
||||
_label = label;
|
||||
if (!_focused)
|
||||
LabelLineEdit.Text = label;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public sealed class ExpendableLightSystem : VisualizerSystem<ExpendableLightComp
|
||||
case ExpendableLightState.Lit:
|
||||
_audioSystem.Stop(comp.PlayingStream);
|
||||
comp.PlayingStream = _audioSystem.PlayPvs(
|
||||
comp.LoopedSound, uid, SharedExpendableLightComponent.LoopedSoundParams)?.Entity;
|
||||
comp.LoopedSound, uid)?.Entity;
|
||||
|
||||
if (args.Sprite.LayerMapTryGet(ExpendableLightVisualLayers.Overlay, out var layerIdx, true))
|
||||
{
|
||||
|
||||
@@ -72,9 +72,6 @@ public sealed class MagicMirrorBoundUserInterface : BoundUserInterface
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_window != null)
|
||||
_window.OnClose -= Close;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
8
Content.Client/MagicMirror/MagicMirrorSystem.cs
Normal file
8
Content.Client/MagicMirror/MagicMirrorSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.MagicMirror;
|
||||
|
||||
namespace Content.Client.MagicMirror;
|
||||
|
||||
public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -35,9 +35,17 @@ public sealed partial class MechMenu : FancyWindow
|
||||
IntegrityDisplayBar.Value = integrityPercent.Float();
|
||||
IntegrityDisplay.Text = Loc.GetString("mech-integrity-display", ("amount", (integrityPercent*100).Int()));
|
||||
|
||||
if (mechComp.MaxEnergy != 0f)
|
||||
{
|
||||
var energyPercent = mechComp.Energy / mechComp.MaxEnergy;
|
||||
EnergyDisplayBar.Value = energyPercent.Float();
|
||||
EnergyDisplay.Text = Loc.GetString("mech-energy-display", ("amount", (energyPercent*100).Int()));
|
||||
}
|
||||
else
|
||||
{
|
||||
EnergyDisplayBar.Value = 0f;
|
||||
EnergyDisplay.Text = Loc.GetString("mech-energy-missing");
|
||||
}
|
||||
|
||||
SlotDisplay.Text = Loc.GetString("mech-slot-display",
|
||||
("amount", mechComp.MaxEquipmentAmount - mechComp.EquipmentContainer.ContainedEntities.Count));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Replays.Loading;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -37,7 +38,7 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem
|
||||
if (mapPos.MapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
var angle = (mapPos.Position - xform.MapPosition.Position).ToWorldAngle();
|
||||
var angle = (mapPos.Position - _transform.GetMapCoordinates(player.Value, xform: xform).Position).ToWorldAngle();
|
||||
|
||||
var curRot = _transform.GetWorldRotation(xform);
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
|
||||
_linkMenu?.Dispose();
|
||||
_listMenu?.Dispose();
|
||||
_configurationMenu?.Dispose();
|
||||
}
|
||||
|
||||
@@ -139,11 +139,13 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
||||
|
||||
public sealed class ClearAllNetworkLinkOverlays : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "clearnetworklinkoverlays";
|
||||
public string Description => "Clear all network link overlays.";
|
||||
public string Help => Command;
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IoCManager.Resolve<IEntityManager>().System<NetworkConfiguratorSystem>().ClearAllOverlays();
|
||||
_e.System<NetworkConfiguratorSystem>().ClearAllOverlays();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.Client.NodeContainer
|
||||
{
|
||||
public sealed class NodeVisCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "nodevis";
|
||||
public string Description => "Toggles node group visualization";
|
||||
public string Help => "";
|
||||
@@ -21,20 +23,22 @@ namespace Content.Client.NodeContainer
|
||||
return;
|
||||
}
|
||||
|
||||
var sys = EntitySystem.Get<NodeGroupSystem>();
|
||||
var sys = _e.System<NodeGroupSystem>();
|
||||
sys.SetVisEnabled(!sys.VisEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NodeVisFilterCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "nodevisfilter";
|
||||
public string Description => "Toggles showing a specific group on nodevis";
|
||||
public string Help => "Usage: nodevis [filter]\nOmit filter to list currently masked-off";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var sys = EntitySystem.Get<NodeGroupSystem>();
|
||||
var sys = _e.System<NodeGroupSystem>();
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
<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'}" />
|
||||
|
||||
@@ -69,6 +69,12 @@ namespace Content.Client.Options.UI.Tabs
|
||||
UpdateApplyButton();
|
||||
};
|
||||
|
||||
ViewportVerticalFitCheckBox.OnToggled += _ =>
|
||||
{
|
||||
UpdateViewportScale();
|
||||
UpdateApplyButton();
|
||||
};
|
||||
|
||||
IntegerScalingCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
ViewportLowResCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
ParallaxLowQualityCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
@@ -84,6 +90,7 @@ 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);
|
||||
@@ -119,6 +126,7 @@ 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);
|
||||
@@ -151,6 +159,7 @@ 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);
|
||||
@@ -166,6 +175,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
isVPStretchSame &&
|
||||
isVPScaleSame &&
|
||||
isIntegerScalingSame &&
|
||||
isVPVerticalFitSame &&
|
||||
isVPResSame &&
|
||||
isPLQSame &&
|
||||
isFpsCounterVisibleSame &&
|
||||
@@ -252,6 +262,8 @@ 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));
|
||||
}
|
||||
|
||||
|
||||
@@ -234,6 +234,7 @@ 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);
|
||||
|
||||
28
Content.Client/Overlays/ShowCriminalRecordIconsSystem.cs
Normal file
28
Content.Client/Overlays/ShowCriminalRecordIconsSystem.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
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,14 +1,13 @@
|
||||
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 IPrototypeManager _prototypeMan = default!;
|
||||
[Dependency] private readonly HungerSystem _hunger = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -17,42 +16,12 @@ public sealed class ShowHungerIconsSystem : EquipmentHudSystem<ShowHungerIconsCo
|
||||
SubscribeLocalEvent<HungerComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent hungerComponent, ref GetStatusIconsEvent args)
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || args.InContainer)
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
|
||||
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;
|
||||
if (_hunger.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
||||
60
Content.Client/Overlays/ShowJobIconsSystem.cs
Normal file
60
Content.Client/Overlays/ShowJobIconsSystem.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
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}");
|
||||
}
|
||||
}
|
||||
28
Content.Client/Overlays/ShowMindShieldIconsSystem.cs
Normal file
28
Content.Client/Overlays/ShowMindShieldIconsSystem.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
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
|
||||
// WD EDIT START
|
||||
{
|
||||
Log.Error($"Invalid job icon prototype: {jobIconToGet}");
|
||||
result.Add(_prototypeMan.Index<StatusIconPrototype>(JobIconForNoId));
|
||||
}
|
||||
// WD EDIT END
|
||||
|
||||
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,10 +1,11 @@
|
||||
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!;
|
||||
@@ -16,28 +17,13 @@ public sealed class ShowSyndicateIconsSystem : EquipmentHudSystem<ShowSyndicateI
|
||||
SubscribeLocalEvent<NukeOperativeComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent nukeOperativeComponent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (!IsActive || args.InContainer)
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.SyndStatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
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 IPrototypeManager _prototypeMan = default!;
|
||||
[Dependency] private readonly ThirstSystem _thirst = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -17,42 +16,12 @@ public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsCo
|
||||
SubscribeLocalEvent<ThirstComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent thirstComponent, ref GetStatusIconsEvent args)
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || args.InContainer)
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
|
||||
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;
|
||||
if (_thirst.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype!);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace Content.Client.PDA
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
SendMessage(new PdaRequestUpdateInterfaceMessage());
|
||||
_menu = new PdaMenu();
|
||||
_menu.OpenCenteredLeft();
|
||||
_menu.OnClose += Close;
|
||||
@@ -32,17 +31,17 @@ namespace Content.Client.PDA
|
||||
|
||||
_menu.EjectIdButton.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaIdSlotId));
|
||||
SendPredictedMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaIdSlotId));
|
||||
};
|
||||
|
||||
_menu.EjectPenButton.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPenSlotId));
|
||||
SendPredictedMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPenSlotId));
|
||||
};
|
||||
|
||||
_menu.EjectPaiButton.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPaiSlotId));
|
||||
SendPredictedMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPaiSlotId));
|
||||
};
|
||||
|
||||
_menu.ActivateMusicButton.OnPressed += _ =>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using Content.Shared.Paper;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Paper;
|
||||
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class PaperComponent : SharedPaperComponent
|
||||
{
|
||||
}
|
||||
[RegisterComponent]
|
||||
public sealed partial class PaperComponent : SharedPaperComponent;
|
||||
|
||||
@@ -24,14 +24,7 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
if (!state.AllChunks!.Contains(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
|
||||
foreach (var beacon in component.Beacons)
|
||||
{
|
||||
if (!state.AllBeacons!.Contains(beacon))
|
||||
component.Beacons.Remove(beacon);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
foreach (var index in component.Chunks.Keys)
|
||||
@@ -39,25 +32,19 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
if (!state.Chunks.ContainsKey(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
|
||||
foreach (var beacon in component.Beacons)
|
||||
{
|
||||
if (!state.Beacons.Contains(beacon))
|
||||
component.Beacons.Remove(beacon);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var ((category, origin), chunk) in state.Chunks)
|
||||
foreach (var (origin, chunk) in state.Chunks)
|
||||
{
|
||||
var newChunk = new NavMapChunk(origin);
|
||||
|
||||
foreach (var (atmosDirection, value) in chunk)
|
||||
newChunk.TileData[atmosDirection] = value;
|
||||
|
||||
component.Chunks[(category, origin)] = newChunk;
|
||||
Array.Copy(chunk, newChunk.TileData, chunk.Length);
|
||||
component.Chunks[origin] = newChunk;
|
||||
}
|
||||
|
||||
foreach (var beacon in state.Beacons)
|
||||
component.Beacons.Add(beacon);
|
||||
component.Beacons.Clear();
|
||||
foreach (var (nuid, beacon) in state.Beacons)
|
||||
{
|
||||
component.Beacons[nuid] = beacon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Content.Shared.Atmos;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Pinpointer.UI;
|
||||
|
||||
@@ -71,10 +72,10 @@ public partial class NavMapControl : MapGridControl
|
||||
protected float BackgroundOpacity = 0.9f;
|
||||
private int _targetFontsize = 8;
|
||||
|
||||
protected Dictionary<(int, Vector2i), (int, Vector2i)> HorizLinesLookup = new();
|
||||
protected Dictionary<(int, Vector2i), (int, Vector2i)> HorizLinesLookupReversed = new();
|
||||
protected Dictionary<(int, Vector2i), (int, Vector2i)> VertLinesLookup = new();
|
||||
protected Dictionary<(int, Vector2i), (int, Vector2i)> VertLinesLookupReversed = new();
|
||||
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;
|
||||
@@ -376,7 +377,7 @@ public partial class NavMapControl : MapGridControl
|
||||
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)
|
||||
foreach (var beacon in _navMap.Beacons.Values)
|
||||
{
|
||||
var position = beacon.Position - offset;
|
||||
position = ScalePosition(position with { Y = -position.Y });
|
||||
@@ -485,101 +486,90 @@ public partial class NavMapControl : MapGridControl
|
||||
return;
|
||||
|
||||
// We'll use the following dictionaries to combine collinear wall lines
|
||||
HorizLinesLookup.Clear();
|
||||
HorizLinesLookupReversed.Clear();
|
||||
VertLinesLookup.Clear();
|
||||
VertLinesLookupReversed.Clear();
|
||||
_horizLines.Clear();
|
||||
_horizLinesReversed.Clear();
|
||||
_vertLines.Clear();
|
||||
_vertLinesReversed.Clear();
|
||||
|
||||
foreach ((var (category, chunkOrigin), var chunk) in _navMap.Chunks)
|
||||
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;
|
||||
|
||||
foreach (var (chunkOrigin, chunk) in _navMap.Chunks)
|
||||
{
|
||||
if (category != NavMapChunkType.Wall)
|
||||
for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
|
||||
{
|
||||
var tileData = chunk.TileData[i] & SharedNavMapSystem.WallMask;
|
||||
if (tileData == 0)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
|
||||
{
|
||||
var value = (ushort) Math.Pow(2, i);
|
||||
var mask = _navMapSystem.GetCombinedEdgesForChunk(chunk.TileData) & value;
|
||||
tileData >>= (int) NavMapChunkType.Wall;
|
||||
|
||||
if (mask == 0x0)
|
||||
continue;
|
||||
|
||||
var relativeTile = SharedNavMapSystem.GetTile(mask);
|
||||
var relativeTile = SharedNavMapSystem.GetTileFromIndex(i);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
|
||||
|
||||
if (!_navMapSystem.AllTileEdgesAreOccupied(chunk.TileData, relativeTile))
|
||||
if (tileData != SharedNavMapSystem.AllDirMask)
|
||||
{
|
||||
AddRectForThinWall(chunk.TileData, tile);
|
||||
AddRectForThinWall(tileData, tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
tile = tile with { Y = -tile.Y };
|
||||
|
||||
NavMapChunk? neighborChunk;
|
||||
bool neighbor;
|
||||
|
||||
// North edge
|
||||
if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
|
||||
{
|
||||
neighbor = _navMap.Chunks.TryGetValue((NavMapChunkType.Wall, chunkOrigin + new Vector2i(0, 1)), out neighborChunk) &&
|
||||
(neighborChunk.TileData[AtmosDirection.South] &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
|
||||
neighbor = (chunk.TileData[AtmosDirection.South] & flag) != 0x0;
|
||||
}
|
||||
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 (!neighbor)
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile + new Vector2i(_grid.TileSize, -_grid.TileSize), HorizLinesLookup, HorizLinesLookupReversed);
|
||||
if ((neighborData & southMask) == 0)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize),
|
||||
tile + new Vector2i(_grid.TileSize, -_grid.TileSize), _horizLines,
|
||||
_horizLinesReversed);
|
||||
}
|
||||
|
||||
// East edge
|
||||
if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
|
||||
{
|
||||
neighbor = _navMap.Chunks.TryGetValue((NavMapChunkType.Wall, chunkOrigin + new Vector2i(1, 0)), out neighborChunk) &&
|
||||
(neighborChunk.TileData[AtmosDirection.West] &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
|
||||
neighbor = (chunk.TileData[AtmosDirection.West] & flag) != 0x0;
|
||||
}
|
||||
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 (!neighbor)
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize), tile + new Vector2i(_grid.TileSize, 0), VertLinesLookup, VertLinesLookupReversed);
|
||||
if ((neighborData & westMask) == 0)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize),
|
||||
tile + new Vector2i(_grid.TileSize, 0), _vertLines, _vertLinesReversed);
|
||||
}
|
||||
|
||||
// South edge
|
||||
if (relativeTile.Y == 0)
|
||||
{
|
||||
neighbor = _navMap.Chunks.TryGetValue((NavMapChunkType.Wall, chunkOrigin + new Vector2i(0, -1)), out neighborChunk) &&
|
||||
(neighborChunk.TileData[AtmosDirection.North] &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, SharedNavMapSystem.ChunkSize - 1))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, -1));
|
||||
neighbor = (chunk.TileData[AtmosDirection.North] & flag) != 0x0;
|
||||
}
|
||||
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 (!neighbor)
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), HorizLinesLookup, HorizLinesLookupReversed);
|
||||
if ((neighborData & northMask) == 0)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), _horizLines,
|
||||
_horizLinesReversed);
|
||||
}
|
||||
|
||||
// West edge
|
||||
if (relativeTile.X == 0)
|
||||
{
|
||||
neighbor = _navMap.Chunks.TryGetValue((NavMapChunkType.Wall, chunkOrigin + new Vector2i(-1, 0)), out neighborChunk) &&
|
||||
(neighborChunk.TileData[AtmosDirection.East] &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(SharedNavMapSystem.ChunkSize - 1, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(-1, 0));
|
||||
neighbor = (chunk.TileData[AtmosDirection.East] & flag) != 0x0;
|
||||
}
|
||||
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 (!neighbor)
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, VertLinesLookup, VertLinesLookupReversed);
|
||||
if ((neighborData & eastMask) == 0)
|
||||
{
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, _vertLines,
|
||||
_vertLinesReversed);
|
||||
}
|
||||
|
||||
// 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)));
|
||||
@@ -587,11 +577,15 @@ public partial class NavMapControl : MapGridControl
|
||||
}
|
||||
|
||||
// Record the combined lines
|
||||
foreach (var (origin, terminal) in HorizLinesLookup)
|
||||
TileLines.Add((origin.Item2, terminal.Item2));
|
||||
foreach (var (origin, terminal) in _horizLines)
|
||||
{
|
||||
TileLines.Add((origin, terminal));
|
||||
}
|
||||
|
||||
foreach (var (origin, terminal) in VertLinesLookup)
|
||||
TileLines.Add((origin.Item2, terminal.Item2));
|
||||
foreach (var (origin, terminal) in _vertLines)
|
||||
{
|
||||
TileLines.Add((origin, terminal));
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateNavMapAirlocks()
|
||||
@@ -599,26 +593,23 @@ public partial class NavMapControl : MapGridControl
|
||||
if (_navMap == null || _grid == null)
|
||||
return;
|
||||
|
||||
foreach (var ((category, _), chunk) in _navMap.Chunks)
|
||||
foreach (var chunk in _navMap.Chunks.Values)
|
||||
{
|
||||
if (category != NavMapChunkType.Airlock)
|
||||
for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
|
||||
{
|
||||
var tileData = chunk.TileData[i] & SharedNavMapSystem.AirlockMask;
|
||||
if (tileData == 0)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
|
||||
{
|
||||
var value = (int) Math.Pow(2, i);
|
||||
var mask = _navMapSystem.GetCombinedEdgesForChunk(chunk.TileData) & value;
|
||||
tileData >>= (int) NavMapChunkType.Airlock;
|
||||
|
||||
if (mask == 0x0)
|
||||
continue;
|
||||
|
||||
var relative = SharedNavMapSystem.GetTile(mask);
|
||||
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 (!_navMapSystem.AllTileEdgesAreOccupied(chunk.TileData, relative))
|
||||
if (tileData != SharedNavMapSystem.AllDirMask)
|
||||
{
|
||||
AddRectForThinAirlock(chunk.TileData, tile);
|
||||
AddRectForThinAirlock(tileData, tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -632,108 +623,90 @@ public partial class NavMapControl : MapGridControl
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRectForThinWall(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
|
||||
private void AddRectForThinWall(int tileData, Vector2i tile)
|
||||
{
|
||||
if (_navMapSystem == null || _grid == null)
|
||||
return;
|
||||
var leftTop = new Vector2(-0.5f, 0.5f - ThinWallThickness);
|
||||
var rightBottom = new Vector2(0.5f, 0.5f);
|
||||
|
||||
var leftTop = new Vector2(-0.5f, -0.5f + ThinWallThickness);
|
||||
var rightBottom = new Vector2(0.5f, -0.5f);
|
||||
|
||||
foreach (var (direction, mask) in tileData)
|
||||
for (var i = 0; i < SharedNavMapSystem.Directions; i++)
|
||||
{
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
|
||||
var flag = (ushort) SharedNavMapSystem.GetFlag(relative);
|
||||
|
||||
if ((mask & flag) == 0)
|
||||
var dirMask = 1 << i;
|
||||
if ((tileData & dirMask) == 0)
|
||||
continue;
|
||||
|
||||
var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
|
||||
var angle = new Angle(0);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case AtmosDirection.East: angle = new Angle(MathF.PI * 0.5f); break;
|
||||
case AtmosDirection.South: angle = new Angle(MathF.PI); break;
|
||||
case AtmosDirection.West: angle = new Angle(MathF.PI * -0.5f); break;
|
||||
}
|
||||
|
||||
// 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(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
|
||||
private void AddRectForThinAirlock(int tileData, Vector2i tile)
|
||||
{
|
||||
if (_navMapSystem == null || _grid == null)
|
||||
return;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
foreach (var (direction, mask) in tileData)
|
||||
for (var i = 0; i < SharedNavMapSystem.Directions; i++)
|
||||
{
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
|
||||
var flag = (ushort) SharedNavMapSystem.GetFlag(relative);
|
||||
|
||||
if ((mask & flag) == 0)
|
||||
var dirMask = 1 << i;
|
||||
if ((tileData & dirMask) == 0)
|
||||
continue;
|
||||
|
||||
var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
|
||||
var angle = new Angle(0);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case AtmosDirection.East: angle = new Angle(MathF.PI * 0.5f);break;
|
||||
case AtmosDirection.South: angle = new Angle(MathF.PI); break;
|
||||
case AtmosDirection.West: angle = new Angle(MathF.PI * -0.5f); break;
|
||||
}
|
||||
|
||||
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,
|
||||
protected void AddOrUpdateNavMapLine(
|
||||
Vector2i origin,
|
||||
Vector2i terminus,
|
||||
Dictionary<(int, Vector2i), (int, Vector2i)> lookup,
|
||||
Dictionary<(int, Vector2i), (int, Vector2i)> lookupReversed,
|
||||
int index = 0)
|
||||
Dictionary<Vector2i, Vector2i> lookup,
|
||||
Dictionary<Vector2i, Vector2i> lookupReversed)
|
||||
{
|
||||
(int, Vector2i) foundTermiusTuple;
|
||||
(int, Vector2i) foundOriginTuple;
|
||||
Vector2i foundTermius;
|
||||
Vector2i foundOrigin;
|
||||
|
||||
if (lookup.TryGetValue((index, terminus), out foundTermiusTuple) &&
|
||||
lookupReversed.TryGetValue((index, origin), out foundOriginTuple))
|
||||
// Does our new line end at the beginning of an existing line?
|
||||
if (lookup.Remove(terminus, out foundTermius))
|
||||
{
|
||||
lookup[foundOriginTuple] = foundTermiusTuple;
|
||||
lookupReversed[foundTermiusTuple] = foundOriginTuple;
|
||||
DebugTools.Assert(lookupReversed[foundTermius] == terminus);
|
||||
|
||||
lookup.Remove((index, terminus));
|
||||
lookupReversed.Remove((index, origin));
|
||||
// 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 if (lookup.TryGetValue((index, terminus), out foundTermiusTuple))
|
||||
{
|
||||
lookup[(index, origin)] = foundTermiusTuple;
|
||||
lookup.Remove((index, terminus));
|
||||
lookupReversed[foundTermiusTuple] = (index, origin);
|
||||
}
|
||||
|
||||
else if (lookupReversed.TryGetValue((index, origin), out foundOriginTuple))
|
||||
{
|
||||
lookupReversed[(index, terminus)] = foundOriginTuple;
|
||||
lookupReversed.Remove(foundOriginTuple);
|
||||
lookup[foundOriginTuple] = (index, terminus);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
lookup.Add((index, origin), (index, terminus));
|
||||
lookupReversed.Add((index, terminus), (index, origin));
|
||||
// Our new line precedes an existing line, extending it further to the left
|
||||
lookup[origin] = foundTermius;
|
||||
lookupReversed[foundTermius] = origin;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
protected Vector2 GetOffset()
|
||||
|
||||
@@ -199,6 +199,15 @@ 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)
|
||||
@@ -208,6 +217,15 @@ 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))
|
||||
|
||||
21
Content.Client/Power/ActivatableUIRequiresPowerSystem.cs
Normal file
21
Content.Client/Power/ActivatableUIRequiresPowerSystem.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Robust.Client.Graphics;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Map.Components;
|
||||
using System.Numerics;
|
||||
using static Content.Shared.Power.SharedPowerMonitoringConsoleSystem;
|
||||
|
||||
namespace Content.Client.Power;
|
||||
|
||||
@@ -26,6 +27,11 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
public List<PowerMonitoringConsoleLine> PowerCableNetwork = new();
|
||||
public List<PowerMonitoringConsoleLine> FocusCableNetwork = new();
|
||||
|
||||
private Dictionary<Vector2i, Vector2i>[] _horizLines = [new(), new(), new()];
|
||||
private Dictionary<Vector2i, Vector2i>[] _horizLinesReversed = [new(), new(), new()];
|
||||
private Dictionary<Vector2i, Vector2i>[] _vertLines = [new(), new(), new()];
|
||||
private Dictionary<Vector2i, Vector2i>[] _vertLinesReversed = [new(), new(), new()];
|
||||
|
||||
private MapGridComponent? _grid;
|
||||
|
||||
public PowerMonitoringConsoleNavMapControl() : base()
|
||||
@@ -182,28 +188,32 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
if (chunks == null)
|
||||
return decodedOutput;
|
||||
|
||||
// We'll use the following dictionaries to combine collinear power cable lines
|
||||
HorizLinesLookup.Clear();
|
||||
HorizLinesLookupReversed.Clear();
|
||||
VertLinesLookup.Clear();
|
||||
VertLinesLookupReversed.Clear();
|
||||
Array.ForEach(_horizLines, x=> x.Clear());
|
||||
Array.ForEach(_horizLinesReversed, x=> x.Clear());
|
||||
Array.ForEach(_vertLines, x=> x.Clear());
|
||||
Array.ForEach(_vertLinesReversed, x=> x.Clear());
|
||||
|
||||
foreach ((var chunkOrigin, var chunk) in chunks)
|
||||
foreach (var (chunkOrigin, chunk) in chunks)
|
||||
{
|
||||
for (int cableIdx = 0; cableIdx < chunk.PowerCableData.Length; cableIdx++)
|
||||
for (var cableIdx = 0; cableIdx < 3; cableIdx++)
|
||||
{
|
||||
var horizLines = _horizLines[cableIdx];
|
||||
var horizLinesReversed = _horizLinesReversed[cableIdx];
|
||||
var vertLines = _vertLines[cableIdx];
|
||||
var vertLinesReversed = _vertLinesReversed[cableIdx];
|
||||
|
||||
var chunkMask = chunk.PowerCableData[cableIdx];
|
||||
|
||||
for (var chunkIdx = 0; chunkIdx < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; chunkIdx++)
|
||||
for (var chunkIdx = 0; chunkIdx < ChunkSize * ChunkSize; chunkIdx++)
|
||||
{
|
||||
var value = (int) Math.Pow(2, chunkIdx);
|
||||
var value = 1 << chunkIdx;
|
||||
var mask = chunkMask & value;
|
||||
|
||||
if (mask == 0x0)
|
||||
continue;
|
||||
|
||||
var relativeTile = SharedNavMapSystem.GetTile(mask);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
|
||||
var relativeTile = GetTileFromIndex(chunkIdx);
|
||||
var tile = (chunk.Origin * ChunkSize + relativeTile) * _grid.TileSize;
|
||||
tile = tile with { Y = -tile.Y };
|
||||
|
||||
PowerCableChunk neighborChunk;
|
||||
@@ -212,39 +222,39 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
// Note: we only check the north and east neighbors
|
||||
|
||||
// East
|
||||
if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
|
||||
if (relativeTile.X == ChunkSize - 1)
|
||||
{
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(1, 0), out neighborChunk) &&
|
||||
(neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||
(neighborChunk.PowerCableData[cableIdx] & GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
|
||||
var flag = GetFlag(relativeTile + new Vector2i(1, 0));
|
||||
neighbor = (chunkMask & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (neighbor)
|
||||
{
|
||||
// Add points
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), HorizLinesLookup, HorizLinesLookupReversed, cableIdx);
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), horizLines, horizLinesReversed);
|
||||
}
|
||||
|
||||
// North
|
||||
if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
|
||||
if (relativeTile.Y == ChunkSize - 1)
|
||||
{
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, 1), out neighborChunk) &&
|
||||
(neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||
(neighborChunk.PowerCableData[cableIdx] & GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
|
||||
var flag = GetFlag(relativeTile + new Vector2i(0, 1));
|
||||
neighbor = (chunkMask & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (neighbor)
|
||||
{
|
||||
// Add points
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, VertLinesLookup, VertLinesLookupReversed, cableIdx);
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, vertLines, vertLinesReversed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,11 +263,25 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
|
||||
var gridOffset = new Vector2(_grid.TileSize * 0.5f, -_grid.TileSize * 0.5f);
|
||||
|
||||
foreach (var (origin, terminal) in HorizLinesLookup)
|
||||
decodedOutput.Add(new PowerMonitoringConsoleLine(origin.Item2 + gridOffset, terminal.Item2 + gridOffset, (PowerMonitoringConsoleLineGroup) origin.Item1));
|
||||
for (var index = 0; index < _horizLines.Length; index++)
|
||||
{
|
||||
var horizLines = _horizLines[index];
|
||||
foreach (var (origin, terminal) in horizLines)
|
||||
{
|
||||
decodedOutput.Add(new PowerMonitoringConsoleLine(origin + gridOffset, terminal + gridOffset,
|
||||
(PowerMonitoringConsoleLineGroup) index));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (origin, terminal) in VertLinesLookup)
|
||||
decodedOutput.Add(new PowerMonitoringConsoleLine(origin.Item2 + gridOffset, terminal.Item2 + gridOffset, (PowerMonitoringConsoleLineGroup) origin.Item1));
|
||||
for (var index = 0; index < _vertLines.Length; index++)
|
||||
{
|
||||
var vertLines = _vertLines[index];
|
||||
foreach (var (origin, terminal) in vertLines)
|
||||
{
|
||||
decodedOutput.Add(new PowerMonitoringConsoleLine(origin + gridOffset, terminal + gridOffset,
|
||||
(PowerMonitoringConsoleLineGroup) index));
|
||||
}
|
||||
}
|
||||
|
||||
return decodedOutput;
|
||||
}
|
||||
|
||||
@@ -561,6 +561,7 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
_controller.UpdateProfile(Profile);
|
||||
_controller.ReloadCharacterUI();
|
||||
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
@@ -820,7 +821,6 @@ namespace Content.Client.Preferences.UI
|
||||
CharacterSlot = _preferencesManager.Preferences.SelectedCharacterIndex;
|
||||
|
||||
UpdateAntagRequirements();
|
||||
UpdateRoleRequirements();
|
||||
UpdateControls();
|
||||
ShowClothes.Pressed = true;
|
||||
}
|
||||
@@ -1350,7 +1350,7 @@ namespace Content.Client.Preferences.UI
|
||||
private void SetBodyType(string newBodyType)
|
||||
{
|
||||
Profile = Profile?.WithBodyType(newBodyType);
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
_needUpdatePreview = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ public sealed class AlignRCDConstruction : PlacementMode
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly RCDSystem _rcdSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
private readonly SharedMapSystem _mapSystem;
|
||||
private readonly RCDSystem _rcdSystem;
|
||||
private readonly SharedTransformSystem _transformSystem;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
|
||||
@@ -32,12 +32,7 @@ public sealed class AlignRCDConstruction : PlacementMode
|
||||
/// </summary>
|
||||
public AlignRCDConstruction(PlacementManager pMan) : base(pMan)
|
||||
{
|
||||
var dependencies = IoCManager.Instance!;
|
||||
_entityManager = dependencies.Resolve<IEntityManager>();
|
||||
_mapManager = dependencies.Resolve<IMapManager>();
|
||||
_playerManager = dependencies.Resolve<IPlayerManager>();
|
||||
_stateManager = dependencies.Resolve<IStateManager>();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
_mapSystem = _entityManager.System<SharedMapSystem>();
|
||||
_rcdSystem = _entityManager.System<RCDSystem>();
|
||||
_transformSystem = _entityManager.System<SharedTransformSystem>();
|
||||
|
||||
@@ -12,8 +12,6 @@ using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Graphics.RSI;
|
||||
using Robust.Shared.Serialization.Manager.Exceptions;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.RCD;
|
||||
@@ -69,7 +67,7 @@ public sealed partial class RCDMenu : RadialMenu
|
||||
tooltip = Loc.GetString(entProto.Name);
|
||||
}
|
||||
|
||||
tooltip = char.ToUpper(tooltip[0]) + tooltip.Remove(0, 1);
|
||||
tooltip = OopsConcat(char.ToUpper(tooltip[0]).ToString(), tooltip.Remove(0, 1));
|
||||
|
||||
var button = new RCDMenuButton()
|
||||
{
|
||||
@@ -119,6 +117,12 @@ public sealed partial class RCDMenu : RadialMenu
|
||||
SendRCDSystemMessageAction += bui.SendRCDSystemMessage;
|
||||
}
|
||||
|
||||
private static string OopsConcat(string a, string b)
|
||||
{
|
||||
// This exists to prevent Roslyn being clever and compiling something that fails sandbox checks.
|
||||
return a + b;
|
||||
}
|
||||
|
||||
private void SetupCategories(RadialContainer main, RCDComponent rcd)
|
||||
{
|
||||
foreach (var categoryId in rcd.CategoryPrototypes)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Radiation.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -14,6 +16,7 @@ namespace Content.Client.Radiation.Overlays
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
private TransformSystem? _transform;
|
||||
|
||||
private const float MaxDist = 15.0f;
|
||||
|
||||
@@ -72,6 +75,8 @@ namespace Content.Client.Radiation.Overlays
|
||||
//Queries all pulses on the map and either adds or removes them from the list of rendered pulses based on whether they should be drawn (in range? on the same z-level/map? pulse entity still exists?)
|
||||
private void RadiationQuery(IEye? currentEye)
|
||||
{
|
||||
_transform ??= _entityManager.System<TransformSystem>();
|
||||
|
||||
if (currentEye == null)
|
||||
{
|
||||
_pulses.Clear();
|
||||
@@ -91,7 +96,7 @@ namespace Content.Client.Radiation.Overlays
|
||||
(
|
||||
_baseShader.Duplicate(),
|
||||
new RadiationShaderInstance(
|
||||
_entityManager.GetComponent<TransformComponent>(pulseEntity).MapPosition,
|
||||
_transform.GetMapCoordinates(pulseEntity),
|
||||
pulse.VisualRange,
|
||||
pulse.StartTime,
|
||||
pulse.VisualDuration
|
||||
@@ -109,7 +114,7 @@ namespace Content.Client.Radiation.Overlays
|
||||
_entityManager.TryGetComponent(pulseEntity, out RadiationPulseComponent? pulse))
|
||||
{
|
||||
var shaderInstance = _pulses[pulseEntity];
|
||||
shaderInstance.instance.CurrentMapCoords = _entityManager.GetComponent<TransformComponent>(pulseEntity).MapPosition;
|
||||
shaderInstance.instance.CurrentMapCoords = _transform.GetMapCoordinates(pulseEntity);
|
||||
shaderInstance.instance.Range = pulse.VisualRange;
|
||||
} else {
|
||||
_pulses[pulseEntity].shd.Dispose();
|
||||
|
||||
@@ -195,9 +195,16 @@ public sealed partial class ReplaySpectatorSystem
|
||||
if (uid != _player.LocalEntity)
|
||||
return;
|
||||
|
||||
if (args.Transform.MapUid != null || args.OldMapId == MapId.Nullspace)
|
||||
if (args.Transform.MapUid != null || args.OldMapId == null)
|
||||
return;
|
||||
|
||||
if (_spectatorData != null)
|
||||
{
|
||||
// Currently scrubbing/setting the replay tick
|
||||
// the observer will get respawned once the state was applied
|
||||
return;
|
||||
}
|
||||
|
||||
// The entity being spectated from was moved to null-space.
|
||||
// This was probably because they were spectating some entity in a client-side replay that left PVS range.
|
||||
// Simple respawn the ghost.
|
||||
|
||||
7
Content.Client/Robotics/Systems/RoboticsConsoleSystem.cs
Normal file
7
Content.Client/Robotics/Systems/RoboticsConsoleSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Robotics.Systems;
|
||||
|
||||
namespace Content.Client.Robotics.Systems;
|
||||
|
||||
public sealed class RoboticsConsoleSystem : SharedRoboticsConsoleSystem
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using Content.Shared.Robotics;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Robotics.UI;
|
||||
|
||||
public sealed class RoboticsConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
public RoboticsConsoleWindow _window = default!;
|
||||
|
||||
public RoboticsConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new RoboticsConsoleWindow(Owner);
|
||||
_window.OnDisablePressed += address =>
|
||||
{
|
||||
SendMessage(new RoboticsConsoleDisableMessage(address));
|
||||
};
|
||||
_window.OnDestroyPressed += address =>
|
||||
{
|
||||
SendMessage(new RoboticsConsoleDestroyMessage(address));
|
||||
};
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not RoboticsConsoleState cast)
|
||||
return;
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
40
Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml
Normal file
40
Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml
Normal file
@@ -0,0 +1,40 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'robotics-console-window-title'}"
|
||||
MinSize="600 450">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<!-- List of borgs -->
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="10 10 10 10">
|
||||
<Label Name="NoCyborgs" Text="{Loc 'robotics-console-no-cyborgs'}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<ScrollContainer Name="CyborgsContainer" VerticalExpand="True" Visible="False">
|
||||
<!-- Populated when loading state -->
|
||||
<ItemList Name="Cyborgs"/>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5"/>
|
||||
|
||||
<!-- Selected borg info -->
|
||||
<Label Name="SelectCyborg" Text="{Loc 'robotics-console-select-cyborg'}" HorizontalAlignment="Center"/>
|
||||
<BoxContainer Name="BorgContainer" Orientation="Vertical" MaxHeight="200" Visible="False">
|
||||
<BoxContainer Margin="5 5 5 5" Orientation="Horizontal">
|
||||
<PanelContainer VerticalExpand="True">
|
||||
<BoxContainer HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextureRect Name="BorgSprite" TextureScale="4 4"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
<PanelContainer VerticalExpand="True" HorizontalExpand="True">
|
||||
<RichTextLabel Name="BorgInfo"/>
|
||||
</PanelContainer>
|
||||
<!-- TODO: button to open camera window for this borg -->
|
||||
</BoxContainer>
|
||||
<controls:StripeBack>
|
||||
<BoxContainer Name="DangerZone" Margin="5" Orientation="Horizontal" HorizontalExpand="True" HorizontalAlignment="Center" Visible="False">
|
||||
<Button Name="DisableButton" Text="{Loc 'robotics-console-disable'}" StyleClasses="OpenRight"/>
|
||||
<Button Name="DestroyButton" Text="{Loc 'robotics-console-destroy'}" StyleClasses="OpenLeft"/>
|
||||
</BoxContainer>
|
||||
<Label Name="LockedMessage" Text="{Loc 'robotics-console-locked-message'}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</controls:StripeBack>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
147
Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs
Normal file
147
Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Robotics;
|
||||
using Content.Shared.Robotics.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Robotics.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RoboticsConsoleWindow : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
private readonly LockSystem _lock;
|
||||
private readonly SpriteSystem _sprite;
|
||||
|
||||
public Action<string>? OnDisablePressed;
|
||||
public Action<string>? OnDestroyPressed;
|
||||
|
||||
private Entity<RoboticsConsoleComponent, LockComponent?> _console;
|
||||
private string? _selected;
|
||||
private Dictionary<string, CyborgControlData> _cyborgs = new();
|
||||
|
||||
public RoboticsConsoleWindow(EntityUid console)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_lock = _entMan.System<LockSystem>();
|
||||
_sprite = _entMan.System<SpriteSystem>();
|
||||
|
||||
_console = (console, _entMan.GetComponent<RoboticsConsoleComponent>(console), null);
|
||||
_entMan.TryGetComponent(_console, out _console.Comp2);
|
||||
|
||||
Cyborgs.OnItemSelected += args =>
|
||||
{
|
||||
if (Cyborgs[args.ItemIndex].Metadata is not string address)
|
||||
return;
|
||||
|
||||
_selected = address;
|
||||
PopulateData();
|
||||
};
|
||||
Cyborgs.OnItemDeselected += _ =>
|
||||
{
|
||||
_selected = null;
|
||||
PopulateData();
|
||||
};
|
||||
|
||||
// these won't throw since buttons are only visible if a borg is selected
|
||||
DisableButton.OnPressed += _ =>
|
||||
{
|
||||
OnDisablePressed?.Invoke(_selected!);
|
||||
};
|
||||
DestroyButton.OnPressed += _ =>
|
||||
{
|
||||
OnDestroyPressed?.Invoke(_selected!);
|
||||
};
|
||||
|
||||
// cant put multiple styles in xaml for some reason
|
||||
DestroyButton.StyleClasses.Add(StyleBase.ButtonCaution);
|
||||
}
|
||||
|
||||
public void UpdateState(RoboticsConsoleState state)
|
||||
{
|
||||
_cyborgs = state.Cyborgs;
|
||||
|
||||
// clear invalid selection
|
||||
if (_selected is {} selected && !_cyborgs.ContainsKey(selected))
|
||||
_selected = null;
|
||||
|
||||
var hasCyborgs = _cyborgs.Count > 0;
|
||||
NoCyborgs.Visible = !hasCyborgs;
|
||||
CyborgsContainer.Visible = hasCyborgs;
|
||||
PopulateCyborgs();
|
||||
|
||||
PopulateData();
|
||||
|
||||
var locked = _lock.IsLocked((_console, _console.Comp2));
|
||||
DangerZone.Visible = !locked;
|
||||
LockedMessage.Visible = locked;
|
||||
}
|
||||
|
||||
private void PopulateCyborgs()
|
||||
{
|
||||
// _selected might get set to null when recreating so copy it first
|
||||
var selected = _selected;
|
||||
Cyborgs.Clear();
|
||||
foreach (var (address, data) in _cyborgs)
|
||||
{
|
||||
var item = Cyborgs.AddItem(data.Name, _sprite.Frame0(data.ChassisSprite!), metadata: address);
|
||||
item.Selected = address == selected;
|
||||
}
|
||||
_selected = selected;
|
||||
}
|
||||
|
||||
private void PopulateData()
|
||||
{
|
||||
if (_selected is not {} selected)
|
||||
{
|
||||
SelectCyborg.Visible = true;
|
||||
BorgContainer.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
SelectCyborg.Visible = false;
|
||||
BorgContainer.Visible = true;
|
||||
|
||||
var data = _cyborgs[selected];
|
||||
var model = data.ChassisName;
|
||||
|
||||
BorgSprite.Texture = _sprite.Frame0(data.ChassisSprite!);
|
||||
|
||||
var batteryColor = data.Charge switch {
|
||||
< 0.2f => "red",
|
||||
< 0.4f => "orange",
|
||||
< 0.6f => "yellow",
|
||||
< 0.8f => "green",
|
||||
_ => "blue"
|
||||
};
|
||||
|
||||
var text = new FormattedMessage();
|
||||
text.PushMarkup(Loc.GetString("robotics-console-model", ("name", model)));
|
||||
text.AddMarkup(Loc.GetString("robotics-console-designation"));
|
||||
text.AddText($" {data.Name}\n"); // prevent players trolling by naming borg [color=red]satan[/color]
|
||||
text.PushMarkup(Loc.GetString("robotics-console-battery", ("charge", (int) (data.Charge * 100f)), ("color", batteryColor)));
|
||||
text.PushMarkup(Loc.GetString("robotics-console-brain", ("brain", data.HasBrain)));
|
||||
text.AddMarkup(Loc.GetString("robotics-console-modules", ("count", data.ModuleCount)));
|
||||
BorgInfo.SetMessage(text);
|
||||
|
||||
// how the turntables
|
||||
DisableButton.Disabled = !data.HasBrain;
|
||||
DestroyButton.Disabled = _timing.CurTime < _console.Comp1.NextDestroy;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
DestroyButton.Disabled = _timing.CurTime < _console.Comp1.NextDestroy;
|
||||
}
|
||||
}
|
||||
51
Content.Client/RoundEnd/RoundEndSummaryUIController.cs
Normal file
51
Content.Client/RoundEnd/RoundEndSummaryUIController.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Content.Client.GameTicking.Managers;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Input;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.RoundEnd;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class RoundEndSummaryUIController : UIController,
|
||||
IOnSystemLoaded<ClientGameTicker>
|
||||
{
|
||||
[Dependency] private readonly IInputManager _input = default!;
|
||||
|
||||
private RoundEndSummaryWindow? _window;
|
||||
|
||||
private void ToggleScoreboardWindow(ICommonSession? session = null)
|
||||
{
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
if (_window.IsOpen)
|
||||
{
|
||||
_window.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
_window.OpenCenteredRight();
|
||||
_window.MoveToFront();
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenRoundEndSummaryWindow(RoundEndMessageEvent message)
|
||||
{
|
||||
// Don't open duplicate windows (mainly for replays).
|
||||
if (_window?.RoundId == message.RoundId)
|
||||
return;
|
||||
|
||||
_window = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundEndText,
|
||||
message.RoundDuration, message.RoundId, message.AllPlayersEndInfo, EntityManager);
|
||||
}
|
||||
|
||||
public void OnSystemLoaded(ClientGameTicker system)
|
||||
{
|
||||
_input.SetInputCommand(ContentKeyFunctions.ToggleRoundEndSummaryWindow,
|
||||
InputCmdHandler.FromDelegate(ToggleScoreboardWindow));
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Message;
|
||||
using Content.Shared.GameTicking;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -20,11 +20,15 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
if (_window is null)
|
||||
{
|
||||
_window = new OfferingWindow();
|
||||
_window.Title = Loc.GetString("salvage-magnet-window-title");
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCenteredLeft();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
@@ -108,4 +112,15 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
|
||||
_window.AddOption(option);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Close();
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
var textDimensions = handle.GetDimensions(Font, text, UIScale);
|
||||
|
||||
handle.DrawCircle(origin, scaledRadius, color, false);
|
||||
handle.DrawString(Font, ScalePosition(new Vector2(0f, -radius)) - new Vector2(0f, textDimensions.Y), text, color);
|
||||
handle.DrawString(Font, ScalePosition(new Vector2(0f, -radius)) - new Vector2(0f, textDimensions.Y), text, UIScale, color);
|
||||
}
|
||||
|
||||
const int gridLinesRadial = 8;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using Content.Shared.Shuttles.BUIStates;
|
||||
using Content.Shared.Shuttles.Systems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -12,7 +13,10 @@ namespace Content.Client.Shuttles.UI;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class DockObject : PanelContainer
|
||||
{
|
||||
[PublicAPI]
|
||||
public event Action? UndockPressed;
|
||||
|
||||
[PublicAPI]
|
||||
public event Action? ViewPressed;
|
||||
|
||||
public BoxContainer ContentsContainer => Contents;
|
||||
|
||||
@@ -25,6 +25,7 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
public readonly EntityUid Entity;
|
||||
public float AccumulatedTime;
|
||||
private string _lastValidName;
|
||||
private List<EntityUid> _modules = new();
|
||||
|
||||
public BorgMenu(EntityUid entity)
|
||||
{
|
||||
@@ -114,7 +115,23 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
("actual", _chassis.ModuleCount),
|
||||
("max", _chassis.MaxModules));
|
||||
|
||||
if (_chassis.ModuleContainer.Count == _modules.Count)
|
||||
{
|
||||
var isSame = true;
|
||||
foreach (var module in _chassis.ModuleContainer.ContainedEntities)
|
||||
{
|
||||
if (_modules.Contains(module))
|
||||
continue;
|
||||
isSame = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isSame)
|
||||
return;
|
||||
}
|
||||
|
||||
ModuleContainer.Children.Clear();
|
||||
_modules.Clear();
|
||||
foreach (var module in _chassis.ModuleContainer.ContainedEntities)
|
||||
{
|
||||
var control = new BorgModuleControl(module, _entity);
|
||||
@@ -123,6 +140,7 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
RemoveModuleButtonPressed?.Invoke(module);
|
||||
};
|
||||
ModuleContainer.AddChild(control);
|
||||
_modules.Add(module);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,4 +180,3 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
NameChanged?.Invoke(_lastValidName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Silicons.Laws;
|
||||
using Content.Shared.Silicons.Laws.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Silicons.Laws.Ui;
|
||||
|
||||
@@ -10,6 +11,7 @@ public sealed class SiliconLawBoundUserInterface : BoundUserInterface
|
||||
[ViewVariables]
|
||||
private SiliconLawMenu? _menu;
|
||||
private EntityUid _owner;
|
||||
private List<SiliconLaw>? _laws;
|
||||
|
||||
public SiliconLawBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
@@ -41,6 +43,23 @@ public sealed class SiliconLawBoundUserInterface : BoundUserInterface
|
||||
if (state is not SiliconLawBuiState msg)
|
||||
return;
|
||||
|
||||
if (_laws != null && _laws.Count == msg.Laws.Count)
|
||||
{
|
||||
var isSame = true;
|
||||
foreach (var law in msg.Laws)
|
||||
{
|
||||
if (_laws.Contains(law))
|
||||
continue;
|
||||
isSame = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isSame)
|
||||
return;
|
||||
}
|
||||
|
||||
_laws = msg.Laws.ToList();
|
||||
|
||||
_menu?.Update(_owner, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Shared.Sprite;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.State;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Client.Sprite;
|
||||
|
||||
@@ -15,6 +16,7 @@ public sealed class SpriteFadeSystem : EntitySystem
|
||||
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private readonly HashSet<FadingSpriteComponent> _comps = new();
|
||||
|
||||
@@ -48,7 +50,7 @@ public sealed class SpriteFadeSystem : EntitySystem
|
||||
spriteQuery.TryGetComponent(player, out var playerSprite))
|
||||
{
|
||||
var fadeQuery = GetEntityQuery<SpriteFadeComponent>();
|
||||
var mapPos = playerXform.MapPosition;
|
||||
var mapPos = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: playerXform);
|
||||
|
||||
// Also want to handle large entities even if they may not be clickable.
|
||||
foreach (var ent in state.GetClickableEntities(mapPos))
|
||||
|
||||
@@ -2,7 +2,4 @@ using Content.Shared.Station;
|
||||
|
||||
namespace Content.Client.Station;
|
||||
|
||||
public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
||||
{
|
||||
|
||||
}
|
||||
public sealed class StationSpawningSystem : SharedStationSpawningSystem;
|
||||
|
||||
13
Content.Client/StationRecords/GeneralRecord.xaml
Normal file
13
Content.Client/StationRecords/GeneralRecord.xaml
Normal file
@@ -0,0 +1,13 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<BoxContainer Orientation="Vertical" Margin="5">
|
||||
<Label Name="RecordName" StyleClasses="LabelBig"/>
|
||||
<Label Name="Age"/>
|
||||
<Label Name="Title"/>
|
||||
<Label Name="Job"/>
|
||||
<Label Name="Species"/>
|
||||
<Label Name="Gender"/>
|
||||
<Label Name="Fingerprint"/>
|
||||
<Label Name="Dna"/>
|
||||
<Button Visible="False" Name="DeleteButton" Text="{Loc 'general-station-record-console-delete'}"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
33
Content.Client/StationRecords/GeneralRecord.xaml.cs
Normal file
33
Content.Client/StationRecords/GeneralRecord.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.StationRecords;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GeneralRecord : Control
|
||||
{
|
||||
public Action<uint>? OnDeletePressed;
|
||||
public GeneralRecord(GeneralStationRecord record, bool canDelete, uint? id)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
RecordName.Text = record.Name;
|
||||
Age.Text = Loc.GetString("general-station-record-console-record-age", ("age", record.Age.ToString()));
|
||||
Title.Text = Loc.GetString("general-station-record-console-record-title",
|
||||
("job", Loc.GetString(record.JobTitle)));
|
||||
Species.Text = Loc.GetString("general-station-record-console-record-species", ("species", record.Species));
|
||||
Gender.Text = Loc.GetString("general-station-record-console-record-gender",
|
||||
("gender", record.Gender.ToString()));
|
||||
Fingerprint.Text = Loc.GetString("general-station-record-console-record-fingerprint",
|
||||
("fingerprint", record.Fingerprint ?? Loc.GetString("generic-not-available-shorthand")));
|
||||
Dna.Text = Loc.GetString("general-station-record-console-record-dna",
|
||||
("dna", record.DNA ?? Loc.GetString("generic-not-available-shorthand")));
|
||||
|
||||
if (canDelete && id != null )
|
||||
{
|
||||
DeleteButton.Visible = true;
|
||||
DeleteButton.OnPressed += _ => OnDeletePressed?.Invoke(id.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public sealed class GeneralStationRecordConsoleBoundUserInterface : BoundUserInt
|
||||
SendMessage(new SelectStationRecord(key));
|
||||
_window.OnFiltersChanged += (type, filterValue) =>
|
||||
SendMessage(new SetStationRecordFilter(type, filterValue));
|
||||
_window.OnDeleted += id => SendMessage(new DeleteStationRecord(id));
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.OpenCentered();
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" Margin="5">
|
||||
<Label Name="RecordContainerStatus" Visible="False" Text="{Loc 'general-station-record-console-select-record-info'}"/>
|
||||
<BoxContainer Name="RecordContainer" Orientation="Vertical" />
|
||||
<Control Name="RecordContainer" Visible="False"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
@@ -24,6 +24,7 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||
public Action<uint?>? OnKeySelected;
|
||||
|
||||
public Action<StationRecordFilterType, string>? OnFiltersChanged;
|
||||
public Action<uint>? OnDeleted;
|
||||
|
||||
private bool _isPopulating;
|
||||
|
||||
@@ -125,11 +126,10 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||
RecordContainerStatus.Text = state.SelectedKey == null
|
||||
? Loc.GetString("general-station-record-console-no-record-found")
|
||||
: Loc.GetString("general-station-record-console-select-record-info");
|
||||
PopulateRecordContainer(state.Record);
|
||||
PopulateRecordContainer(state.Record, state.CanDeleteEntries, state.SelectedKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
RecordContainer.DisposeAllChildren();
|
||||
RecordContainer.RemoveAllChildren();
|
||||
}
|
||||
}
|
||||
@@ -151,101 +151,13 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||
RecordListing.SortItemsByText();
|
||||
}
|
||||
|
||||
private void PopulateRecordContainer(GeneralStationRecord record)
|
||||
private void PopulateRecordContainer(GeneralStationRecord record, bool enableDelete, uint? id)
|
||||
{
|
||||
RecordContainer.DisposeAllChildren();
|
||||
RecordContainer.RemoveAllChildren();
|
||||
// sure
|
||||
var recordControls = new Control[]
|
||||
{
|
||||
new Label()
|
||||
{
|
||||
Text = record.Name,
|
||||
StyleClasses = { "LabelBig" }
|
||||
},
|
||||
SetupCharacterSpriteView(record),
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-age", ("age", record.Age.ToString()))
|
||||
var newRecord = new GeneralRecord(record, enableDelete, id);
|
||||
newRecord.OnDeletePressed = OnDeleted;
|
||||
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-title", ("job", Loc.GetString(record.JobTitle)))
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-species", ("species", record.Species))
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-gender", ("gender", record.Gender.ToString()))
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-fingerprint", ("fingerprint", record.Fingerprint ?? Loc.GetString("generic-not-available-shorthand")))
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-dna", ("dna", record.DNA ?? Loc.GetString("generic-not-available-shorthand")))
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var control in recordControls)
|
||||
{
|
||||
RecordContainer.AddChild(control);
|
||||
}
|
||||
}
|
||||
|
||||
private BoxContainer SetupCharacterSpriteView(GeneralStationRecord record)
|
||||
{
|
||||
IEntityManager entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
IPrototypeManager prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
HumanoidAppearanceSystem appearanceSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<HumanoidAppearanceSystem>();
|
||||
|
||||
entityManager.DeleteEntity(_previewDummy);
|
||||
|
||||
var profile = record.Profile ?? new HumanoidCharacterProfile();
|
||||
_previewDummy = entityManager.SpawnEntity(prototypeManager.Index<SpeciesPrototype>(profile.Species).DollPrototype, MapCoordinates.Nullspace);
|
||||
appearanceSystem.LoadProfile(_previewDummy, profile);
|
||||
GiveDummyJobClothes(_previewDummy, record.JobPrototype, profile);
|
||||
|
||||
var spriteViewBox = new BoxContainer();
|
||||
// var sprite = entityManager.GetComponent<SpriteComponent>(_previewDummy);
|
||||
|
||||
spriteViewBox.AddChild(new SpriteView(_previewDummy, entityManager) { Scale = new Vector2(5, 5)});
|
||||
spriteViewBox.AddChild(new SpriteView(_previewDummy, entityManager) { Scale = new Vector2(5, 5), OverrideDirection = Direction.East});
|
||||
|
||||
return spriteViewBox;
|
||||
}
|
||||
private void GiveDummyJobClothes(EntityUid dummy, string jobPrototype, HumanoidCharacterProfile profile)
|
||||
{
|
||||
IEntityManager entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
IPrototypeManager prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
ClientInventorySystem inventorySystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ClientInventorySystem>();
|
||||
|
||||
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?)
|
||||
var job = prototypeManager.Index<JobPrototype>(jobPrototype ?? SharedGameTicker.FallbackOverflowJob);
|
||||
|
||||
if (job.StartingGear != null && inventorySystem.TryGetSlots(dummy, out var slots))
|
||||
{
|
||||
var gear = prototypeManager.Index<StartingGearPrototype>(job.StartingGear);
|
||||
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var itemType = gear.GetGear(slot.Name);
|
||||
if (inventorySystem.TryUnequip(dummy, slot.Name, out var unequippedItem, true, true))
|
||||
{
|
||||
entityManager.DeleteEntity(unequippedItem.Value);
|
||||
}
|
||||
|
||||
if (itemType != string.Empty)
|
||||
{
|
||||
var item = entityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
|
||||
inventorySystem.TryEquip(dummy, item, slot.Name, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
RecordContainer.AddChild(newRecord);
|
||||
}
|
||||
|
||||
private void FilterListingOfRecords(string text = "")
|
||||
@@ -260,4 +172,5 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||
{
|
||||
return Loc.GetString($"general-station-record-{type.ToString().ToLower()}-filter");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,14 @@ public sealed class StorageBoundUserInterface : BoundUserInterface
|
||||
_storage = _entManager.System<StorageSystem>();
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
if (_entManager.TryGetComponent<StorageComponent>(Owner, out var comp))
|
||||
_storage.OpenStorageWindow((Owner, comp));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
@@ -25,16 +33,5 @@ public sealed class StorageBoundUserInterface : BoundUserInterface
|
||||
|
||||
_storage.CloseStorageWindow(Owner);
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
base.ReceiveMessage(message);
|
||||
|
||||
if (message is StorageModifyWindowMessage)
|
||||
{
|
||||
if (_entManager.TryGetComponent<StorageComponent>(Owner, out var comp))
|
||||
_storage.OpenStorageWindow((Owner, comp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user