Puddles & spreader refactor (#15191)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
MinSize="270 420"
|
||||
SetSize="315 420" Title="{Loc 'gas-analyzer-window-name'}">
|
||||
SetSize="360 420" Title="{Loc 'gas-analyzer-window-name'}">
|
||||
<BoxContainer Orientation="Vertical" Margin="5 5 5 5">
|
||||
<BoxContainer Name="CTopBox" Orientation="Horizontal"/>
|
||||
<!-- Gas Mix Data, Populated by function -->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -261,11 +262,9 @@ namespace Content.Client.Atmos.UI
|
||||
// This is the gas bar thingy
|
||||
var height = 30;
|
||||
var minSize = 24; // This basically allows gases which are too small, to be shown properly
|
||||
var gasBar = new BoxContainer
|
||||
var gasBar = new SplitBar()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true,
|
||||
MinSize = new Vector2(0, height)
|
||||
MinHeight = height
|
||||
};
|
||||
// Separator
|
||||
dataContainer.AddChild(new Control
|
||||
@@ -299,25 +298,10 @@ namespace Content.Client.Atmos.UI
|
||||
});
|
||||
|
||||
// Add to the gas bar //TODO: highlight the currently hover one
|
||||
var left = (j == 0) ? 0f : 2f;
|
||||
var right = (j == gasMix.Gases.Length - 1) ? 0f : 2f;
|
||||
gasBar.AddChild(new PanelContainer
|
||||
{
|
||||
ToolTip = Loc.GetString("gas-analyzer-window-molarity-percentage-text",
|
||||
("gasName", gas.Name),
|
||||
("amount", $"{gas.Amount:0.##}"),
|
||||
("percentage", $"{(gas.Amount / totalGasAmount * 100):0.#}")),
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = gas.Amount,
|
||||
MouseFilter = MouseFilterMode.Stop,
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = color,
|
||||
PaddingLeft = left,
|
||||
PaddingRight = right
|
||||
},
|
||||
MinSize = new Vector2(minSize, 0)
|
||||
});
|
||||
gasBar.AddEntry(gas.Amount, color, tooltip: Loc.GetString("gas-analyzer-window-molarity-percentage-text",
|
||||
("gasName", gas.Name),
|
||||
("amount", $"{gas.Amount:0.##}"),
|
||||
("percentage", $"{(gas.Amount / totalGasAmount * 100):0.#}")));
|
||||
}
|
||||
|
||||
dataContainer.AddChild(gasBar);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using Content.Shared.Foam;
|
||||
using Content.Shared.Smoking;
|
||||
using Content.Shared.Spawners.Components;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Chemistry.Visualizers;
|
||||
|
||||
@@ -9,23 +12,48 @@ namespace Content.Client.Chemistry.Visualizers;
|
||||
/// </summary>
|
||||
public sealed class FoamVisualizerSystem : VisualizerSystem<FoamVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<FoamVisualsComponent, ComponentInit>(OnComponentInit);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
var query = EntityQueryEnumerator<FoamVisualsComponent, TimedDespawnComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp, out var despawn))
|
||||
{
|
||||
if (despawn.Lifetime > 1f)
|
||||
continue;
|
||||
|
||||
// Despawn animation.
|
||||
if (TryComp(uid, out AnimationPlayerComponent? animPlayer)
|
||||
&& !AnimationSystem.HasRunningAnimation(uid, animPlayer, FoamVisualsComponent.AnimationKey))
|
||||
{
|
||||
AnimationSystem.Play(uid, animPlayer, comp.Animation, FoamVisualsComponent.AnimationKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the animation used by foam visuals when the foam dissolves.
|
||||
/// </summary>
|
||||
private void OnComponentInit(EntityUid uid, FoamVisualsComponent comp, ComponentInit args)
|
||||
{
|
||||
comp.Animation = new Animation()
|
||||
comp.Animation = new Animation
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(comp.AnimationTime),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick()
|
||||
new AnimationTrackSpriteFlick
|
||||
{
|
||||
LayerKey = FoamVisualLayers.Base,
|
||||
KeyFrames =
|
||||
@@ -36,25 +64,6 @@ public sealed class FoamVisualizerSystem : VisualizerSystem<FoamVisualsComponent
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays the animation used by foam visuals when the foam dissolves.
|
||||
/// </summary>
|
||||
protected override void OnAppearanceChange(EntityUid uid, FoamVisualsComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (AppearanceSystem.TryGetData<bool>(uid, FoamVisuals.State, out var state, args.Component) && state)
|
||||
{
|
||||
if (TryComp(uid, out AnimationPlayerComponent? animPlayer)
|
||||
&& !AnimationSystem.HasRunningAnimation(uid, animPlayer, FoamVisualsComponent.AnimationKey))
|
||||
AnimationSystem.Play(uid, animPlayer, comp.Animation, FoamVisualsComponent.AnimationKey);
|
||||
}
|
||||
|
||||
if (AppearanceSystem.TryGetData<Color>(uid, FoamVisuals.Color, out var color, args.Component))
|
||||
{
|
||||
if (args.Sprite != null)
|
||||
args.Sprite.Color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum FoamVisualLayers : byte
|
||||
|
||||
@@ -5,7 +5,8 @@ using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Fluids;
|
||||
|
||||
public sealed class MoppingSystem : SharedMoppingSystem
|
||||
/// <inheritdoc/>
|
||||
public sealed class AbsorbentSystem : SharedAbsorbentSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
67
Content.Client/Fluids/PuddleSystem.cs
Normal file
67
Content.Client/Fluids/PuddleSystem.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Content.Client.IconSmoothing;
|
||||
using Content.Shared.Fluids;
|
||||
using Content.Shared.Fluids.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Fluids;
|
||||
|
||||
public sealed class PuddleSystem : SharedPuddleSystem
|
||||
{
|
||||
[Dependency] private readonly IconSmoothSystem _smooth = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PuddleComponent, AppearanceChangeEvent>(OnPuddleAppearance);
|
||||
}
|
||||
|
||||
private void OnPuddleAppearance(EntityUid uid, PuddleComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
float volume = 1f;
|
||||
|
||||
if (args.AppearanceData.TryGetValue(PuddleVisuals.CurrentVolume, out var volumeObj))
|
||||
{
|
||||
volume = (float) volumeObj;
|
||||
}
|
||||
|
||||
// Update smoothing and sprite based on volume.
|
||||
if (TryComp<IconSmoothComponent>(uid, out var smooth))
|
||||
{
|
||||
if (volume < LowThreshold)
|
||||
{
|
||||
args.Sprite.LayerSetState(0, $"{smooth.StateBase}a");
|
||||
_smooth.SetEnabled(uid, false, smooth);
|
||||
}
|
||||
else if (volume < 0.6f)
|
||||
{
|
||||
args.Sprite.LayerSetState(0, $"{smooth.StateBase}b");
|
||||
_smooth.SetEnabled(uid, false, smooth);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!smooth.Enabled)
|
||||
{
|
||||
args.Sprite.LayerSetState(0, $"{smooth.StateBase}0");
|
||||
_smooth.SetEnabled(uid, true, smooth);
|
||||
_smooth.DirtyNeighbours(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var baseColor = Color.White;
|
||||
|
||||
if (args.AppearanceData.TryGetValue(PuddleVisuals.SolutionColor, out var colorObj))
|
||||
{
|
||||
var color = (Color) colorObj;
|
||||
args.Sprite.Color = color * baseColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Sprite.Color *= baseColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Content.Client.Fluids
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class PuddleVisualizerComponent : Component
|
||||
{
|
||||
// Whether the underlying solution color should be used. True in most cases.
|
||||
[DataField("recolor")] public bool Recolor = true;
|
||||
|
||||
// Whether the puddle has a unique sprite we don't want to overwrite
|
||||
[DataField("customPuddleSprite")] public bool CustomPuddleSprite;
|
||||
|
||||
// Puddles may change which RSI they use for their sprites (e.g. wet floor effects). This field will store the original RSI they used.
|
||||
[DataField("originalRsi")] public RSI? OriginalRsi;
|
||||
|
||||
/// <summary>
|
||||
/// Puddles with volume below this threshold are able to have their sprite changed to a wet floor effect, though this is not the only factor.
|
||||
/// </summary>
|
||||
[DataField("wetFloorEffectThreshold")]
|
||||
public FixedPoint2 WetFloorEffectThreshold = FixedPoint2.New(5);
|
||||
|
||||
/// <summary>
|
||||
/// Alpha (opacity) of the wet floor sparkle effect. Higher alpha = more opaque/visible.
|
||||
/// </summary>
|
||||
[DataField("wetFloorEffectAlpha")]
|
||||
public float WetFloorEffectAlpha = 0.75f; //should be somewhat transparent by default.
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Fluids;
|
||||
using Content.Shared.FixedPoint;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Client.Fluids
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class PuddleVisualizerSystem : VisualizerSystem<PuddleVisualizerComponent>
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PuddleVisualizerComponent, ComponentInit>(OnComponentInit);
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, PuddleVisualizerComponent puddleVisuals, ComponentInit args)
|
||||
{
|
||||
if (!TryComp(uid, out SpriteComponent? sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
puddleVisuals.OriginalRsi = sprite.BaseRSI; //Back up the original RSI upon initialization
|
||||
RandomizeState(sprite, puddleVisuals.OriginalRsi);
|
||||
RandomizeRotation(sprite);
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, PuddleVisualizerComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
{
|
||||
Logger.Warning($"Missing SpriteComponent for PuddleVisualizerSystem on entityUid = {uid}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AppearanceSystem.TryGetData<float>(uid, PuddleVisuals.VolumeScale, out var volumeScale)
|
||||
|| !AppearanceSystem.TryGetData<FixedPoint2>(uid, PuddleVisuals.CurrentVolume, out var currentVolume)
|
||||
|| !AppearanceSystem.TryGetData<Color>(uid, PuddleVisuals.SolutionColor, out var solutionColor)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, PuddleVisuals.IsEvaporatingVisual, out var isEvaporating))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// volumeScale is our opacity based on level of fullness to overflow. The lower bound is hard-capped for visibility reasons.
|
||||
var cappedScale = Math.Min(1.0f, volumeScale * 0.75f + 0.25f);
|
||||
|
||||
var newColor = component.Recolor ? solutionColor.WithAlpha(cappedScale) : args.Sprite.Color.WithAlpha(cappedScale);
|
||||
|
||||
args.Sprite.LayerSetColor(0, newColor);
|
||||
|
||||
// Don't consider wet floor effects if we're using a custom sprite.
|
||||
if (component.CustomPuddleSprite)
|
||||
return;
|
||||
|
||||
if (isEvaporating && currentVolume <= component.WetFloorEffectThreshold)
|
||||
{
|
||||
// If we need the effect but don't already have it - start it
|
||||
if (args.Sprite.LayerGetState(0) != "sparkles")
|
||||
StartWetFloorEffect(args.Sprite, component.WetFloorEffectAlpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have the effect but don't need it - end it
|
||||
if (args.Sprite.LayerGetState(0) == "sparkles")
|
||||
EndWetFloorEffect(args.Sprite, component.OriginalRsi);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartWetFloorEffect(SpriteComponent sprite, float alpha)
|
||||
{
|
||||
sprite.LayerSetState(0, "sparkles", "Fluids/wet_floor_sparkles.rsi");
|
||||
sprite.Color = sprite.Color.WithAlpha(alpha);
|
||||
sprite.LayerSetAutoAnimated(0, false);
|
||||
sprite.LayerSetAutoAnimated(0, true); //fixes a bug where the sparkle effect would sometimes freeze on a single frame.
|
||||
}
|
||||
|
||||
private void EndWetFloorEffect(SpriteComponent sprite, RSI? originalRSI)
|
||||
{
|
||||
RandomizeState(sprite, originalRSI);
|
||||
sprite.LayerSetAutoAnimated(0, false);
|
||||
}
|
||||
|
||||
private void RandomizeState(SpriteComponent sprite, RSI? rsi)
|
||||
{
|
||||
var maxStates = rsi?.ToArray();
|
||||
if (maxStates is not { Length: > 0 }) return;
|
||||
|
||||
var selectedState = _random.Next(0, maxStates.Length - 1); //randomly select an index for which RSI state to use.
|
||||
sprite.LayerSetState(0, maxStates[selectedState].StateId, rsi); // sets the sprite's state via our randomly selected index.
|
||||
}
|
||||
|
||||
private void RandomizeRotation(SpriteComponent sprite)
|
||||
{
|
||||
float rotationDegrees = _random.Next(0, 359); // randomly select a rotation for our puddle sprite.
|
||||
sprite.Rotation = Angle.FromDegrees(rotationDegrees); // sets the sprite's rotation to the one we randomly selected.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,4 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<ProgressBar
|
||||
HorizontalExpand="True"
|
||||
Name="PercentBar"
|
||||
MinSize="20 20"
|
||||
VerticalAlignment="Center"
|
||||
Margin="2 8 4 2"
|
||||
MaxValue="1.0"
|
||||
MinValue="0.0">
|
||||
</ProgressBar>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
<controls:SplitBar xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Name="Bar">
|
||||
</controls:SplitBar>
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
using Content.Shared.Fluids;
|
||||
using System.Linq;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Fluids;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Fluids.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AbsorbentItemStatus : Control
|
||||
public sealed partial class AbsorbentItemStatus : SplitBar
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly EntityUid _uid;
|
||||
private Dictionary<Color, float> _progress = new();
|
||||
|
||||
public AbsorbentItemStatus(EntityUid uid, IEntityManager entManager)
|
||||
{
|
||||
@@ -25,7 +29,23 @@ namespace Content.Client.Fluids.UI
|
||||
if (!_entManager.TryGetComponent<AbsorbentComponent>(_uid, out var absorbent))
|
||||
return;
|
||||
|
||||
PercentBar.Value = absorbent.Progress;
|
||||
var oldProgress = _progress.ShallowClone();
|
||||
_progress.Clear();
|
||||
|
||||
foreach (var item in absorbent.Progress)
|
||||
{
|
||||
_progress[item.Key] = item.Value;
|
||||
}
|
||||
|
||||
if (oldProgress.OrderBy(x => x.Key.ToArgb()).SequenceEqual(_progress))
|
||||
return;
|
||||
|
||||
Bar.Clear();
|
||||
|
||||
foreach (var (key, value) in absorbent.Progress)
|
||||
{
|
||||
Bar.AddEntry(value, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ namespace Content.Client.IconSmoothing
|
||||
[RegisterComponent]
|
||||
public sealed class IconSmoothComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("enabled")]
|
||||
public bool Enabled = true;
|
||||
|
||||
public (EntityUid?, Vector2i)? LastPosition;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,6 +3,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Map.Enumerators;
|
||||
using static Robust.Client.GameObjects.SpriteComponent;
|
||||
|
||||
namespace Content.Client.IconSmoothing
|
||||
@@ -21,6 +22,15 @@ namespace Content.Client.IconSmoothing
|
||||
|
||||
private int _generation;
|
||||
|
||||
public void SetEnabled(EntityUid uid, bool value, IconSmoothComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false) || value == component.Enabled)
|
||||
return;
|
||||
|
||||
component.Enabled = value;
|
||||
DirtyNeighbours(uid, component);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -67,6 +77,7 @@ namespace Content.Client.IconSmoothing
|
||||
|
||||
private void OnShutdown(EntityUid uid, IconSmoothComponent component, ComponentShutdown args)
|
||||
{
|
||||
_dirtyEntities.Enqueue(uid);
|
||||
DirtyNeighbours(uid, component);
|
||||
}
|
||||
|
||||
@@ -139,28 +150,28 @@ namespace Content.Client.IconSmoothing
|
||||
}
|
||||
|
||||
// Yes, we updates ALL smoothing entities surrounding us even if they would never smooth with us.
|
||||
DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(1, 0)));
|
||||
DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(-1, 0)));
|
||||
DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(0, 1)));
|
||||
DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(0, -1)));
|
||||
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(1, 0)));
|
||||
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(-1, 0)));
|
||||
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(0, 1)));
|
||||
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(0, -1)));
|
||||
|
||||
if (comp.Mode is IconSmoothingMode.Corners or IconSmoothingMode.NoSprite or IconSmoothingMode.Diagonal)
|
||||
{
|
||||
DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(1, 1)));
|
||||
DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(-1, -1)));
|
||||
DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(-1, 1)));
|
||||
DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(1, -1)));
|
||||
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(1, 1)));
|
||||
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(-1, -1)));
|
||||
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(-1, 1)));
|
||||
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(1, -1)));
|
||||
}
|
||||
}
|
||||
|
||||
private void DirtyEntities(IEnumerable<EntityUid> entities)
|
||||
private void DirtyEntities(AnchoredEntitiesEnumerator entities)
|
||||
{
|
||||
// Instead of doing HasComp -> Enqueue -> TryGetComp, we will just enqueue all entities. Generally when
|
||||
// dealing with walls neighboring anchored entities will also be walls, and in those instances that will
|
||||
// require one less component fetch/check.
|
||||
foreach (var entity in entities)
|
||||
while (entities.MoveNext(out var entity))
|
||||
{
|
||||
_dirtyEntities.Enqueue(entity);
|
||||
_dirtyEntities.Enqueue(entity.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,9 +195,10 @@ namespace Content.Client.IconSmoothing
|
||||
// Generation on the component is set after an update so we can cull updates that happened this generation.
|
||||
if (!smoothQuery.Resolve(uid, ref smooth, false)
|
||||
|| smooth.Mode == IconSmoothingMode.NoSprite
|
||||
|| smooth.UpdateGeneration == _generation)
|
||||
|| smooth.UpdateGeneration == _generation ||
|
||||
!smooth.Enabled)
|
||||
{
|
||||
if (smooth != null &&
|
||||
if (smooth is { Enabled: true } &&
|
||||
TryComp<SmoothEdgeComponent>(uid, out var edge) &&
|
||||
xformQuery.TryGetComponent(uid, out xform))
|
||||
{
|
||||
@@ -196,13 +208,13 @@ namespace Content.Client.IconSmoothing
|
||||
{
|
||||
var pos = grid.TileIndicesFor(xform.Coordinates);
|
||||
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.North)), smoothQuery))
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.North)), smoothQuery))
|
||||
directions |= DirectionFlag.North;
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.South)), smoothQuery))
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.South)), smoothQuery))
|
||||
directions |= DirectionFlag.South;
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.East)), smoothQuery))
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.East)), smoothQuery))
|
||||
directions |= DirectionFlag.East;
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.West)), smoothQuery))
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.West)), smoothQuery))
|
||||
directions |= DirectionFlag.West;
|
||||
}
|
||||
|
||||
@@ -218,7 +230,7 @@ namespace Content.Client.IconSmoothing
|
||||
if (!spriteQuery.TryGetComponent(uid, out var sprite))
|
||||
{
|
||||
Logger.Error($"Encountered a icon-smoothing entity without a sprite: {ToPrettyString(uid)}");
|
||||
RemComp(uid, smooth);
|
||||
RemCompDeferred(uid, smooth);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -270,7 +282,7 @@ namespace Content.Client.IconSmoothing
|
||||
for (var i = 0; i < neighbors.Length; i++)
|
||||
{
|
||||
var neighbor = (Vector2i) rotation.RotateVec(neighbors[i]);
|
||||
matching = matching && MatchingEntity(smooth, grid.GetAnchoredEntities(pos + neighbor), smoothQuery);
|
||||
matching = matching && MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos + neighbor), smoothQuery);
|
||||
}
|
||||
|
||||
if (matching)
|
||||
@@ -294,13 +306,13 @@ namespace Content.Client.IconSmoothing
|
||||
}
|
||||
|
||||
var pos = grid.TileIndicesFor(xform.Coordinates);
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.North)), smoothQuery))
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.North)), smoothQuery))
|
||||
dirs |= CardinalConnectDirs.North;
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.South)), smoothQuery))
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.South)), smoothQuery))
|
||||
dirs |= CardinalConnectDirs.South;
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.East)), smoothQuery))
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.East)), smoothQuery))
|
||||
dirs |= CardinalConnectDirs.East;
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.West)), smoothQuery))
|
||||
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.West)), smoothQuery))
|
||||
dirs |= CardinalConnectDirs.West;
|
||||
|
||||
sprite.LayerSetState(0, $"{smooth.StateBase}{(int) dirs}");
|
||||
@@ -319,12 +331,16 @@ namespace Content.Client.IconSmoothing
|
||||
CalculateEdge(sprite.Owner, directions, sprite);
|
||||
}
|
||||
|
||||
private bool MatchingEntity(IconSmoothComponent smooth, IEnumerable<EntityUid> candidates, EntityQuery<IconSmoothComponent> smoothQuery)
|
||||
private bool MatchingEntity(IconSmoothComponent smooth, AnchoredEntitiesEnumerator candidates, EntityQuery<IconSmoothComponent> smoothQuery)
|
||||
{
|
||||
foreach (var entity in candidates)
|
||||
while (candidates.MoveNext(out var entity))
|
||||
{
|
||||
if (smoothQuery.TryGetComponent(entity, out var other) && other.SmoothKey == smooth.SmoothKey)
|
||||
if (smoothQuery.TryGetComponent(entity, out var other) &&
|
||||
other.SmoothKey == smooth.SmoothKey &&
|
||||
other.Enabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -366,14 +382,14 @@ namespace Content.Client.IconSmoothing
|
||||
private (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(MapGridComponent grid, IconSmoothComponent smooth, TransformComponent xform, EntityQuery<IconSmoothComponent> smoothQuery)
|
||||
{
|
||||
var pos = grid.TileIndicesFor(xform.Coordinates);
|
||||
var n = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.North)), smoothQuery);
|
||||
var ne = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.NorthEast)), smoothQuery);
|
||||
var e = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.East)), smoothQuery);
|
||||
var se = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.SouthEast)), smoothQuery);
|
||||
var s = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.South)), smoothQuery);
|
||||
var sw = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.SouthWest)), smoothQuery);
|
||||
var w = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.West)), smoothQuery);
|
||||
var nw = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.NorthWest)), smoothQuery);
|
||||
var n = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.North)), smoothQuery);
|
||||
var ne = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.NorthEast)), smoothQuery);
|
||||
var e = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.East)), smoothQuery);
|
||||
var se = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.SouthEast)), smoothQuery);
|
||||
var s = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.South)), smoothQuery);
|
||||
var sw = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.SouthWest)), smoothQuery);
|
||||
var w = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.West)), smoothQuery);
|
||||
var nw = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.NorthWest)), smoothQuery);
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
var cornerNE = CornerFill.None;
|
||||
|
||||
7
Content.Client/UserInterface/Controls/SplitBar.xaml
Normal file
7
Content.Client/UserInterface/Controls/SplitBar.xaml
Normal file
@@ -0,0 +1,7 @@
|
||||
<controls:SplitBar xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
MouseFilter="Stop"
|
||||
Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
MinSize="0 30">
|
||||
</controls:SplitBar>
|
||||
39
Content.Client/UserInterface/Controls/SplitBar.xaml.cs
Normal file
39
Content.Client/UserInterface/Controls/SplitBar.xaml.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class SplitBar : BoxContainer
|
||||
{
|
||||
public SplitBar()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
DisposeAllChildren();
|
||||
}
|
||||
|
||||
public void AddEntry(float amount, Color color, string? tooltip = null)
|
||||
{
|
||||
AddChild(new PanelContainer
|
||||
{
|
||||
ToolTip = tooltip,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = amount,
|
||||
MouseFilter = MouseFilterMode.Stop,
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = color,
|
||||
PaddingLeft = 2f,
|
||||
PaddingRight = 2f,
|
||||
},
|
||||
MinSize = new Vector2(24, 0)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public sealed partial class MeleeWeaponSystem
|
||||
/// <summary>
|
||||
/// Does all of the melee effects for a player that are predicted, i.e. character lunge and weapon animation.
|
||||
/// </summary>
|
||||
public override void DoLunge(EntityUid user, Angle angle, Vector2 localPos, string? animation)
|
||||
public override void DoLunge(EntityUid user, Angle angle, Vector2 localPos, string? animation, bool predicted = true)
|
||||
{
|
||||
if (!Timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user