Merge remote-tracking branch 'upstream/master' into fucking-upstream

This commit is contained in:
Jabak
2024-08-24 13:13:56 +03:00
2222 changed files with 247186 additions and 50848 deletions

View File

@@ -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 ####

View File

@@ -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;

View File

@@ -2,6 +2,4 @@
namespace Content.Client.Access;
public sealed class IdCardSystem : SharedIdCardSystem
{
}
public sealed class IdCardSystem : SharedIdCardSystem;

View File

@@ -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)

View File

@@ -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++;
}
}

View File

@@ -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);
}

View File

@@ -3,7 +3,7 @@ using Robust.Shared.GameStates;
namespace Content.Client.Administration.Components;
[RegisterComponent, NetworkedComponent]
[RegisterComponent]
public sealed partial class HeadstandComponent : SharedHeadstandComponent
{

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,7 @@
using Content.Shared.Administration;
namespace Content.Client.Administration.Systems;
public sealed class AdminFrozenSystem : SharedAdminFrozenSystem
{
}

View File

@@ -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);
}
}
}

View File

@@ -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();

View 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;
}

View File

@@ -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]

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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";
}

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -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();

View 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);
}

View File

@@ -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);

View File

@@ -2,6 +2,4 @@ using Content.Shared.CriminalRecords.Systems;
namespace Content.Client.CriminalRecords.Systems;
public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleSystem
{
}
public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleSystem;

View File

@@ -0,0 +1,5 @@
using Content.Shared.CriminalRecords.Systems;
namespace Content.Client.CriminalRecords.Systems;
public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSystem;

View File

@@ -0,0 +1,5 @@
using Content.Shared.CriminalRecords.Systems;
namespace Content.Client.CriminalRecords.Systems;
public sealed class CriminalRecordsSystem : SharedCriminalRecordsSystem;

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -0,0 +1,8 @@
using Content.Shared.Radio.EntitySystems;
namespace Content.Client.DeviceNetwork;
public sealed class JammerSystem : SharedJammerSystem
{
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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()

View File

@@ -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;

View File

@@ -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);
}

View 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");
}
}
}

View File

@@ -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()

View File

@@ -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})";

View File

@@ -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");
}
}

View File

@@ -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);
}
}
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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)
);

View File

@@ -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;

View File

@@ -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)));
}
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);
}
}
}

View 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);
}

View 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();
}
}
}

View File

@@ -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)

View File

@@ -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;
}
}

View File

@@ -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))
{

View File

@@ -72,9 +72,6 @@ public sealed class MagicMirrorBoundUserInterface : BoundUserInterface
if (!disposing)
return;
if (_window != null)
_window.OnClose -= Close;
_window?.Dispose();
}
}

View File

@@ -0,0 +1,8 @@
using Content.Shared.MagicMirror;
namespace Content.Client.MagicMirror;
public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
{
}

View File

@@ -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));

View File

@@ -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);

View File

@@ -88,6 +88,7 @@ public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
base.Dispose(disposing);
if (!disposing) return;
_linkMenu?.Dispose();
_listMenu?.Dispose();
_configurationMenu?.Dispose();
}

View File

@@ -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();
}
}

View File

@@ -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)
{

View File

@@ -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'}" />

View File

@@ -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));
}

View File

@@ -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);

View 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);
}
}

View File

@@ -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);
}
}

View 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}");
}
}

View 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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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!);
}
}

View File

@@ -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 += _ =>

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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()

View File

@@ -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))

View 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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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>();

View File

@@ -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)

View File

@@ -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();

View File

@@ -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.

View File

@@ -0,0 +1,7 @@
using Content.Shared.Robotics.Systems;
namespace Content.Client.Robotics.Systems;
public sealed class RoboticsConsoleSystem : SharedRoboticsConsoleSystem
{
}

View File

@@ -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();
}
}

View 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>

View 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;
}
}

View 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));
}
}

View File

@@ -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;

View File

@@ -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();
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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))

View File

@@ -2,7 +2,4 @@ using Content.Shared.Station;
namespace Content.Client.Station;
public sealed class StationSpawningSystem : SharedStationSpawningSystem
{
}
public sealed class StationSpawningSystem : SharedStationSpawningSystem;

View 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>

View 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);
}
}
}

View File

@@ -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();

View File

@@ -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>

View File

@@ -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");
}
}

View File

@@ -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