Merge remote-tracking branch 'refs/remotes/upstream/master' into upstream-sync

# Conflicts:
#	Resources/Prototypes/GameRules/events.yml
This commit is contained in:
Morb0
2024-06-02 19:57:53 +03:00
205 changed files with 2243 additions and 1715 deletions

View File

@@ -147,7 +147,7 @@ public sealed partial class BanPanel : DefaultWindow
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
foreach (var proto in prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
{
CreateRoleGroup(proto.ID, proto.Roles, proto.Color);
CreateRoleGroup(proto.ID, proto.Roles.Select(p => p.Id), proto.Color);
}
CreateRoleGroup("Antagonist", prototypeManager.EnumeratePrototypes<AntagPrototype>().Select(p => p.ID), Color.Red);

View File

@@ -25,7 +25,7 @@ public sealed class ExplosionDebugOverlay : Overlay
public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace;
public Matrix3 SpaceMatrix;
public Matrix3x2 SpaceMatrix;
public MapId Map;
private readonly Font _font;
@@ -78,7 +78,8 @@ public sealed class ExplosionDebugOverlay : Overlay
if (SpaceTiles == null)
return;
gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds);
Matrix3x2.Invert(SpaceMatrix, out var invSpace);
gridBounds = invSpace.TransformBox(args.WorldBounds);
DrawText(handle, gridBounds, SpaceMatrix, SpaceTiles, SpaceTileSize);
}
@@ -86,7 +87,7 @@ public sealed class ExplosionDebugOverlay : Overlay
private void DrawText(
DrawingHandleScreen handle,
Box2 gridBounds,
Matrix3 transform,
Matrix3x2 transform,
Dictionary<int, List<Vector2i>> tileSets,
ushort tileSize)
{
@@ -103,7 +104,7 @@ public sealed class ExplosionDebugOverlay : Overlay
if (!gridBounds.Contains(centre))
continue;
var worldCenter = transform.Transform(centre);
var worldCenter = Vector2.Transform(centre, transform);
var screenCenter = _eyeManager.WorldToScreen(worldCenter);
@@ -119,7 +120,7 @@ public sealed class ExplosionDebugOverlay : Overlay
if (tileSets.TryGetValue(0, out var set))
{
var epicenter = set.First();
var worldCenter = transform.Transform((epicenter + Vector2Helpers.Half) * tileSize);
var worldCenter = Vector2.Transform((epicenter + Vector2Helpers.Half) * tileSize, transform);
var screenCenter = _eyeManager.WorldToScreen(worldCenter) + new Vector2(-24, -24);
var text = $"{Intensity[0]:F2}\nΣ={TotalIntensity:F1}\nΔ={Slope:F1}";
handle.DrawString(_font, screenCenter, text);
@@ -148,11 +149,12 @@ public sealed class ExplosionDebugOverlay : Overlay
if (SpaceTiles == null)
return;
gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds).Enlarged(2);
Matrix3x2.Invert(SpaceMatrix, out var invSpace);
gridBounds = invSpace.TransformBox(args.WorldBounds).Enlarged(2);
handle.SetTransform(SpaceMatrix);
DrawTiles(handle, gridBounds, SpaceTiles, SpaceTileSize);
handle.SetTransform(Matrix3.Identity);
handle.SetTransform(Matrix3x2.Identity);
}
private void DrawTiles(

View File

@@ -66,7 +66,7 @@ public sealed class AtmosDebugOverlay : Overlay
DrawData(msg, handle);
}
handle.SetTransform(Matrix3.Identity);
handle.SetTransform(Matrix3x2.Identity);
}
private void DrawData(DebugMessage msg,

View File

@@ -190,7 +190,7 @@ namespace Content.Client.Atmos.Overlays
var (_, _, worldMatrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
state.drawHandle.SetTransform(worldMatrix);
var floatBounds = invMatrix.TransformBox(in state.WorldBounds).Enlarged(grid.TileSize);
var floatBounds = invMatrix.TransformBox(state.WorldBounds).Enlarged(grid.TileSize);
var localBounds = new Box2i(
(int) MathF.Floor(floatBounds.Left),
(int) MathF.Floor(floatBounds.Bottom),
@@ -249,7 +249,7 @@ namespace Content.Client.Atmos.Overlays
});
drawHandle.UseShader(null);
drawHandle.SetTransform(Matrix3.Identity);
drawHandle.SetTransform(Matrix3x2.Identity);
}
private void DrawMapOverlay(

View File

@@ -1,7 +1,8 @@
using System.Numerics;
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Speech;
using Content.Shared.Whitelist;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
@@ -19,6 +20,7 @@ public sealed partial class EmotesMenu : RadialMenu
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
private readonly SpriteSystem _spriteSystem;
private readonly EntityWhitelistSystem _whitelistSystem;
public event Action<ProtoId<EmotePrototype>>? OnPlayEmote;
@@ -28,6 +30,7 @@ public sealed partial class EmotesMenu : RadialMenu
RobustXamlLoader.Load(this);
_spriteSystem = _entManager.System<SpriteSystem>();
_whitelistSystem = _entManager.System<EntityWhitelistSystem>();
var main = FindControl<RadialContainer>("Main");
@@ -37,8 +40,8 @@ public sealed partial class EmotesMenu : RadialMenu
var player = _playerManager.LocalSession?.AttachedEntity;
if (emote.Category == EmoteCategory.Invalid ||
emote.ChatTriggers.Count == 0 ||
!(player.HasValue && (emote.Whitelist?.IsValid(player.Value, _entManager) ?? true)) ||
(emote.Blacklist?.IsValid(player.Value, _entManager) ?? false))
!(player.HasValue && _whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) ||
_whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value))
continue;
if (!emote.Available &&

View File

@@ -38,9 +38,9 @@ namespace Content.Client.Clickable
renderOrder = sprite.RenderOrder;
var (spritePos, spriteRot) = transform.GetWorldPositionRotation(xformQuery);
var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye.Rotation);
bottom = Matrix3.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom;
bottom = Matrix3Helpers.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom;
var invSpriteMatrix = Matrix3.Invert(sprite.GetLocalMatrix());
Matrix3x2.Invert(sprite.GetLocalMatrix(), out var invSpriteMatrix);
// This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites.
var relativeRotation = (spriteRot + eye.Rotation).Reduced().FlipPositive();
@@ -48,8 +48,8 @@ namespace Content.Client.Clickable
Angle cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero;
// First we get `localPos`, the clicked location in the sprite-coordinate frame.
var entityXform = Matrix3.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping);
var localPos = invSpriteMatrix.Transform(entityXform.Transform(worldPos));
var entityXform = Matrix3Helpers.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping);
var localPos = Vector2.Transform(Vector2.Transform(worldPos, entityXform), invSpriteMatrix);
// Check explicitly defined click-able bounds
if (CheckDirBound(sprite, relativeRotation, localPos))
@@ -79,8 +79,8 @@ namespace Content.Client.Clickable
// convert to layer-local coordinates
layer.GetLayerDrawMatrix(dir, out var matrix);
var inverseMatrix = Matrix3.Invert(matrix);
var layerLocal = inverseMatrix.Transform(localPos);
Matrix3x2.Invert(matrix, out var inverseMatrix);
var layerLocal = Vector2.Transform(localPos, inverseMatrix);
// Convert to image coordinates
var layerImagePos = (Vector2i) (layerLocal * EyeManager.PixelsPerMeter * new Vector2(1, -1) + rsiState.Size / 2f);

View File

@@ -2,6 +2,7 @@ using System.Linq;
using Content.Client.UserInterface.Systems.MenuBar.Widgets;
using Content.Shared.Construction.Prototypes;
using Content.Shared.Tag;
using Content.Shared.Whitelist;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Placement;
@@ -23,6 +24,7 @@ namespace Content.Client.Construction.UI
/// </summary>
internal sealed class ConstructionMenuPresenter : IDisposable
{
[Dependency] private readonly EntityManager _entManager = default!;
[Dependency] private readonly IEntitySystemManager _systemManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlacementManager _placementManager = default!;
@@ -30,6 +32,7 @@ namespace Content.Client.Construction.UI
[Dependency] private readonly IPlayerManager _playerManager = default!;
private readonly IConstructionMenuView _constructionView;
private readonly EntityWhitelistSystem _whitelistSystem;
private ConstructionSystem? _constructionSystem;
private ConstructionPrototype? _selected;
@@ -78,6 +81,7 @@ namespace Content.Client.Construction.UI
// This is a lot easier than a factory
IoCManager.InjectDependencies(this);
_constructionView = new ConstructionMenu();
_whitelistSystem = _entManager.System<EntityWhitelistSystem>();
// This is required so that if we load after the system is initialized, we can bind to it immediately
if (_systemManager.TryGetEntitySystem<ConstructionSystem>(out var constructionSystem))
@@ -157,7 +161,7 @@ namespace Content.Client.Construction.UI
if (_playerManager.LocalSession == null
|| _playerManager.LocalEntity == null
|| (recipe.EntityWhitelist != null && !recipe.EntityWhitelist.IsValid(_playerManager.LocalEntity.Value)))
|| _whitelistSystem.IsWhitelistFail(recipe.EntityWhitelist, _playerManager.LocalEntity.Value))
continue;
if (!string.IsNullOrEmpty(search))

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Shared.Decals;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -113,7 +114,7 @@ namespace Content.Client.Decals.Overlays
handle.DrawTexture(cache.Texture, decal.Coordinates, angle, decal.Color);
}
handle.SetTransform(Matrix3.Identity);
handle.SetTransform(Matrix3x2.Identity);
}
}
}

View File

@@ -51,7 +51,7 @@ public sealed class DecalPlacementOverlay : Overlay
var handle = args.WorldHandle;
handle.SetTransform(worldMatrix);
var localPos = invMatrix.Transform(mousePos.Position);
var localPos = Vector2.Transform(mousePos.Position, invMatrix);
if (snap)
{
@@ -63,6 +63,6 @@ public sealed class DecalPlacementOverlay : Overlay
var box = new Box2Rotated(aabb, rotation, localPos);
handle.DrawTextureRect(_sprite.Frame0(decal.Sprite), box, color);
handle.SetTransform(Matrix3.Identity);
handle.SetTransform(Matrix3x2.Identity);
}
}

View File

@@ -56,8 +56,8 @@ public sealed class DoAfterOverlay : Overlay
// If you use the display UI scale then need to set max(1f, displayscale) because 0 is valid.
const float scale = 1f;
var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
var rotationMatrix = Matrix3.CreateRotation(-rotation);
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale));
var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation);
var curTime = _timing.CurTime;
@@ -91,9 +91,9 @@ public sealed class DoAfterOverlay : Overlay
? curTime - _meta.GetPauseTime(uid, meta)
: curTime;
var worldMatrix = Matrix3.CreateTranslation(worldPosition);
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
var worldMatrix = Matrix3Helpers.CreateTranslation(worldPosition);
var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix);
var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld);
handle.SetTransform(matty);
var offset = 0f;
@@ -151,7 +151,7 @@ public sealed class DoAfterOverlay : Overlay
}
handle.UseShader(null);
handle.SetTransform(Matrix3.Identity);
handle.SetTransform(Matrix3x2.Identity);
}
public Color GetProgressColor(float progress, float alpha = 1f)

View File

@@ -1,7 +1,9 @@
using Content.Shared.CCVar;
using Content.Shared.Drugs;
using Content.Shared.StatusEffect;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
@@ -10,6 +12,7 @@ namespace Content.Client.Drugs;
public sealed class RainbowOverlay : Overlay
{
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
@@ -75,6 +78,10 @@ public sealed class RainbowOverlay : Overlay
protected override void Draw(in OverlayDrawArgs args)
{
// TODO disable only the motion part or ike's idea (single static frame of the overlay)
if (_config.GetCVar(CCVars.ReducedMotion))
return;
if (ScreenTexture == null)
return;

View File

@@ -155,7 +155,6 @@ namespace Content.Client.Entry
_parallaxManager.LoadDefaultParallax();
_overlayManager.AddOverlay(new SingularityOverlay());
_overlayManager.AddOverlay(new FlashOverlay());
_overlayManager.AddOverlay(new RadiationPulseOverlay());
_chatManager.Initialize();
_clientPreferencesManager.Initialize();

View File

@@ -48,7 +48,7 @@ public sealed class ExplosionOverlay : Overlay
DrawExplosion(drawHandle, args.WorldBounds, visuals, index, xforms, textures);
}
drawHandle.SetTransform(Matrix3.Identity);
drawHandle.SetTransform(Matrix3x2.Identity);
drawHandle.UseShader(null);
}
@@ -78,7 +78,8 @@ public sealed class ExplosionOverlay : Overlay
if (visuals.SpaceTiles == null)
return;
gridBounds = Matrix3.Invert(visuals.SpaceMatrix).TransformBox(worldBounds).Enlarged(2);
Matrix3x2.Invert(visuals.SpaceMatrix, out var invSpace);
gridBounds = invSpace.TransformBox(worldBounds).Enlarged(2);
drawHandle.SetTransform(visuals.SpaceMatrix);
DrawTiles(drawHandle, gridBounds, index, visuals.SpaceTiles, visuals, visuals.SpaceTileSize, textures);

View File

@@ -1,12 +1,11 @@
using System.Numerics;
using Content.Shared.Flash;
using Content.Shared.Flash.Components;
using Content.Shared.StatusEffect;
using Content.Client.Viewport;
using Robust.Client.Graphics;
using Robust.Client.State;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Graphics;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using SixLabors.ImageSharp.PixelFormats;
@@ -17,66 +16,87 @@ namespace Content.Client.Flash
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IClyde _displayManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
private readonly StatusEffectsSystem _statusSys;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly ShaderInstance _shader;
private double _startTime = -1;
private double _lastsFor = 1;
private Texture? _screenshotTexture;
public float PercentComplete = 0.0f;
public Texture? ScreenshotTexture;
public FlashOverlay()
{
IoCManager.InjectDependencies(this);
_shader = _prototypeManager.Index<ShaderPrototype>("FlashedEffect").Instance().Duplicate();
_shader = _prototypeManager.Index<ShaderPrototype>("FlashedEffect").InstanceUnique();
_statusSys = _entityManager.System<StatusEffectsSystem>();
}
public void ReceiveFlash(double duration)
protected override void FrameUpdate(FrameEventArgs args)
{
var playerEntity = _playerManager.LocalEntity;
if (playerEntity == null)
return;
if (!_entityManager.HasComponent<FlashedComponent>(playerEntity)
|| !_entityManager.TryGetComponent<StatusEffectsComponent>(playerEntity, out var status))
return;
if (!_statusSys.TryGetTime(playerEntity.Value, SharedFlashSystem.FlashedKey, out var time, status))
return;
var curTime = _timing.CurTime;
var lastsFor = (float) (time.Value.Item2 - time.Value.Item1).TotalSeconds;
var timeDone = (float) (curTime - time.Value.Item1).TotalSeconds;
PercentComplete = timeDone / lastsFor;
}
public void ReceiveFlash()
{
if (_stateManager.CurrentState is IMainViewportState state)
{
// take a screenshot
// note that the callback takes a while and ScreenshotTexture will be null the first few Draws
state.Viewport.Viewport.Screenshot(image =>
{
var rgba32Image = image.CloneAs<Rgba32>(SixLabors.ImageSharp.Configuration.Default);
_screenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image);
ScreenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image);
});
}
}
_startTime = _gameTiming.CurTime.TotalSeconds;
_lastsFor = duration;
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
return false;
if (args.Viewport.Eye != eyeComp.Eye)
return false;
return PercentComplete < 1.0f;
}
protected override void Draw(in OverlayDrawArgs args)
{
if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
return;
if (args.Viewport.Eye != eyeComp.Eye)
return;
var percentComplete = (float) ((_gameTiming.CurTime.TotalSeconds - _startTime) / _lastsFor);
if (percentComplete >= 1.0f)
if (ScreenshotTexture == null)
return;
var worldHandle = args.WorldHandle;
_shader.SetParameter("percentComplete", PercentComplete);
worldHandle.UseShader(_shader);
_shader.SetParameter("percentComplete", percentComplete);
if (_screenshotTexture != null)
{
worldHandle.DrawTextureRectRegion(_screenshotTexture, args.WorldBounds);
}
worldHandle.DrawTextureRectRegion(ScreenshotTexture, args.WorldBounds);
worldHandle.UseShader(null);
}
protected override void DisposeBehavior()
{
base.DisposeBehavior();
_screenshotTexture = null;
ScreenshotTexture = null;
PercentComplete = 1.0f;
}
}
}

View File

@@ -1,62 +1,67 @@
using Content.Shared.Flash;
using Content.Shared.Flash.Components;
using Content.Shared.StatusEffect;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.GameStates;
using Robust.Shared.Timing;
using Robust.Shared.Player;
namespace Content.Client.Flash
namespace Content.Client.Flash;
public sealed class FlashSystem : SharedFlashSystem
{
public sealed class FlashSystem : SharedFlashSystem
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private FlashOverlay _overlay = default!;
public override void Initialize()
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IOverlayManager _overlayManager = default!;
base.Initialize();
public override void Initialize()
SubscribeLocalEvent<FlashedComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<FlashedComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<FlashedComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<FlashedComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<FlashedComponent, StatusEffectAddedEvent>(OnStatusAdded);
_overlay = new();
}
private void OnPlayerAttached(EntityUid uid, FlashedComponent component, LocalPlayerAttachedEvent args)
{
_overlayMan.AddOverlay(_overlay);
}
private void OnPlayerDetached(EntityUid uid, FlashedComponent component, LocalPlayerDetachedEvent args)
{
_overlay.PercentComplete = 1.0f;
_overlay.ScreenshotTexture = null;
_overlayMan.RemoveOverlay(_overlay);
}
private void OnInit(EntityUid uid, FlashedComponent component, ComponentInit args)
{
if (_player.LocalEntity == uid)
{
base.Initialize();
SubscribeLocalEvent<FlashableComponent, ComponentHandleState>(OnFlashableHandleState);
_overlayMan.AddOverlay(_overlay);
}
}
private void OnFlashableHandleState(EntityUid uid, FlashableComponent component, ref ComponentHandleState args)
private void OnShutdown(EntityUid uid, FlashedComponent component, ComponentShutdown args)
{
if (_player.LocalEntity == uid)
{
if (args.Current is not FlashableComponentState state)
return;
_overlay.PercentComplete = 1.0f;
_overlay.ScreenshotTexture = null;
_overlayMan.RemoveOverlay(_overlay);
}
}
// Yes, this code is awful. I'm just porting it to an entity system so don't blame me.
if (_playerManager.LocalEntity != uid)
{
return;
}
if (state.Time == default)
{
return;
}
// Few things here:
// 1. If a shorter duration flash is applied then don't do anything
// 2. If the client-side time is later than when the flash should've ended don't do anything
var currentTime = _gameTiming.CurTime.TotalSeconds;
var newEndTime = state.Time.TotalSeconds + state.Duration;
var currentEndTime = component.LastFlash.TotalSeconds + component.Duration;
if (currentEndTime > newEndTime)
{
return;
}
if (currentTime > newEndTime)
{
return;
}
component.LastFlash = state.Time;
component.Duration = state.Duration;
var overlay = _overlayManager.GetOverlay<FlashOverlay>();
overlay.ReceiveFlash(component.Duration);
private void OnStatusAdded(EntityUid uid, FlashedComponent component, StatusEffectAddedEvent args)
{
if (_player.LocalEntity == uid && args.Key == FlashedKey)
{
_overlay.ReceiveFlash();
}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.FixedPoint;
using System.Numerics;
using Content.Shared.FixedPoint;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
@@ -73,7 +74,7 @@ public sealed class PuddleOverlay : Overlay
}
}
drawHandle.SetTransform(Matrix3.Identity);
drawHandle.SetTransform(Matrix3x2.Identity);
}
private void DrawScreen(in OverlayDrawArgs args)
@@ -99,7 +100,7 @@ public sealed class PuddleOverlay : Overlay
if (!gridBounds.Contains(centre))
continue;
var screenCenter = _eyeManager.WorldToScreen(matrix.Transform(centre));
var screenCenter = _eyeManager.WorldToScreen(Vector2.Transform(centre, matrix));
drawHandle.DrawString(_font, screenCenter, debugOverlayData.CurrentVolume.ToString(), Color.White);
}

View File

@@ -11,6 +11,9 @@ public sealed class Box : BoxContainer, IDocumentTag
HorizontalExpand = true;
control = this;
if (args.TryGetValue("Margin", out var margin))
Margin = new Thickness(float.Parse(margin));
if (args.TryGetValue("Orientation", out var orientation))
Orientation = Enum.Parse<LayoutOrientation>(orientation);
else

View File

@@ -0,0 +1,49 @@
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.Guidebook.Richtext;
[UsedImplicitly]
public sealed class ColorBox : PanelContainer, IDocumentTag
{
public bool TryParseTag(Dictionary<string, string> args, [NotNullWhen(true)] out Control? control)
{
HorizontalExpand = true;
VerticalExpand = true;
control = this;
if (args.TryGetValue("Margin", out var margin))
Margin = new Thickness(float.Parse(margin));
if (args.TryGetValue("HorizontalAlignment", out var halign))
HorizontalAlignment = Enum.Parse<HAlignment>(halign);
else
HorizontalAlignment = HAlignment.Stretch;
if (args.TryGetValue("VerticalAlignment", out var valign))
VerticalAlignment = Enum.Parse<VAlignment>(valign);
else
VerticalAlignment = VAlignment.Stretch;
var styleBox = new StyleBoxFlat();
if (args.TryGetValue("Color", out var color))
styleBox.BackgroundColor = Color.FromHex(color);
if (args.TryGetValue("OutlineThickness", out var outlineThickness))
styleBox.BorderThickness = new Thickness(float.Parse(outlineThickness));
else
styleBox.BorderThickness = new Thickness(1);
if (args.TryGetValue("OutlineColor", out var outlineColor))
styleBox.BorderColor = Color.FromHex(outlineColor);
else
styleBox.BorderColor = Color.White;
PanelOverride = styleBox;
return true;
}
}

View File

@@ -0,0 +1,27 @@
using System.Diagnostics.CodeAnalysis;
using Content.Client.UserInterface.Controls;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client.Guidebook.Richtext;
[UsedImplicitly]
public sealed class Table : TableContainer, IDocumentTag
{
public bool TryParseTag(Dictionary<string, string> args, [NotNullWhen(true)] out Control? control)
{
HorizontalExpand = true;
control = this;
if (!args.TryGetValue("Columns", out var columns) || !int.TryParse(columns, out var columnsCount))
{
Logger.Error("Guidebook tag \"Table\" does not specify required property \"Columns.\"");
control = null;
return false;
}
Columns = columnsCount;
return true;
}
}

View File

@@ -733,8 +733,17 @@ namespace Content.Client.Lobby.UI
_jobPriorities.Clear();
var firstCategory = true;
var departments = _prototypeManager.EnumeratePrototypes<DepartmentPrototype>().ToArray();
Array.Sort(departments, DepartmentUIComparer.Instance);
// Get all displayed departments
var departments = new List<DepartmentPrototype>();
foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
{
if (department.EditorHidden)
continue;
departments.Add(department);
}
departments.Sort(DepartmentUIComparer.Instance);
var items = new[]
{
@@ -788,7 +797,7 @@ namespace Content.Client.Lobby.UI
JobList.AddChild(category);
}
var jobs = department.Roles.Select(jobId => _prototypeManager.Index<JobPrototype>(jobId))
var jobs = department.Roles.Select(jobId => _prototypeManager.Index(jobId))
.Where(job => job.SetPreference)
.ToArray();
@@ -835,13 +844,15 @@ namespace Content.Client.Lobby.UI
if (jobId == job.ID)
{
other.Select(selectedPrio);
continue;
}
else if (selectedJobPrio == JobPriority.High && (JobPriority) other.Selected == JobPriority.High)
{
// Lower any other high priorities to medium.
other.Select((int) JobPriority.Medium);
Profile = Profile?.WithJobPriority(jobId, JobPriority.Medium);
}
if (selectedJobPrio != JobPriority.High || (JobPriority) other.Selected != JobPriority.High)
continue;
// Lower any other high priorities to medium.
other.Select((int)JobPriority.Medium);
Profile = Profile?.WithJobPriority(jobId, JobPriority.Medium);
}
// TODO: Only reload on high change (either to or from).
@@ -946,6 +957,11 @@ namespace Content.Client.Lobby.UI
SetDirty();
ReloadPreview();
};
if (Profile is null)
return;
UpdateJobPriorities();
}
private void OnFlavorTextChange(string content)

View File

@@ -101,7 +101,7 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
if (!_mapManager.TryFindGridAt(mousePos, out var gridUid, out var grid))
return;
StartDragging(gridUid, Transform(gridUid).InvWorldMatrix.Transform(mousePos.Position));
StartDragging(gridUid, Vector2.Transform(mousePos.Position, Transform(gridUid).InvWorldMatrix));
}
if (!TryComp(_dragging, out TransformComponent? xform))
@@ -116,7 +116,7 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
return;
}
var localToWorld = xform.WorldMatrix.Transform(_localPosition);
var localToWorld = Vector2.Transform(_localPosition, xform.WorldMatrix);
if (localToWorld.EqualsApprox(mousePos.Position, 0.01f)) return;

View File

@@ -223,7 +223,7 @@ namespace Content.Client.NPC
foreach (var crumb in chunk.Value)
{
var crumbMapPos = worldMatrix.Transform(_system.GetCoordinate(chunk.Key, crumb.Coordinates));
var crumbMapPos = Vector2.Transform(_system.GetCoordinate(chunk.Key, crumb.Coordinates), worldMatrix);
var distance = (crumbMapPos - mouseWorldPos.Position).Length();
if (distance < nearestDistance)
@@ -292,7 +292,7 @@ namespace Content.Client.NPC
foreach (var poly in tile)
{
if (poly.Box.Contains(invGridMatrix.Transform(mouseWorldPos.Position)))
if (poly.Box.Contains(Vector2.Transform(mouseWorldPos.Position, invGridMatrix)))
{
nearest = poly;
break;
@@ -488,7 +488,7 @@ namespace Content.Client.NPC
if (neighborMap.MapId != args.MapId)
continue;
neighborPos = invMatrix.Transform(neighborMap.Position);
neighborPos = Vector2.Transform(neighborMap.Position, invMatrix);
}
else
{
@@ -576,7 +576,7 @@ namespace Content.Client.NPC
}
}
worldHandle.SetTransform(Matrix3.Identity);
worldHandle.SetTransform(Matrix3x2.Identity);
}
}
}

View File

@@ -199,7 +199,7 @@ namespace Content.Client.NodeContainer
}
handle.SetTransform(Matrix3.Identity);
handle.SetTransform(Matrix3x2.Identity);
_gridIndex.Clear();
}

View File

@@ -42,8 +42,8 @@ public sealed class EntityHealthBarOverlay : Overlay
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
const float scale = 1f;
var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
var rotationMatrix = Matrix3.CreateRotation(-rotation);
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale));
var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation);
var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>();
while (query.MoveNext(out var uid,
@@ -83,10 +83,10 @@ public sealed class EntityHealthBarOverlay : Overlay
continue;
var worldPosition = _transform.GetWorldPosition(xform);
var worldMatrix = Matrix3.CreateTranslation(worldPosition);
var worldMatrix = Matrix3Helpers.CreateTranslation(worldPosition);
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix);
var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld);
handle.SetTransform(matty);
@@ -115,7 +115,7 @@ public sealed class EntityHealthBarOverlay : Overlay
handle.DrawRect(pixelDarken, Black.WithAlpha(128));
}
handle.SetTransform(Matrix3.Identity);
handle.SetTransform(Matrix3x2.Identity);
}
/// <summary>

View File

@@ -7,7 +7,7 @@ namespace Content.Client.Overlays;
public sealed partial class StencilOverlay
{
private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeComponent rangeComp, Matrix3 invMatrix)
private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeComponent rangeComp, Matrix3x2 invMatrix)
{
var worldHandle = args.WorldHandle;
var renderScale = args.Viewport.RenderScale.X;
@@ -16,7 +16,7 @@ public sealed partial class StencilOverlay
var length = zoom.X;
var bufferRange = MathF.Min(10f, rangeComp.Range);
var pixelCenter = invMatrix.Transform(rangeComp.Origin);
var pixelCenter = Vector2.Transform(rangeComp.Origin, invMatrix);
// Something something offset?
var vertical = args.Viewport.Size.Y;
@@ -44,7 +44,7 @@ public sealed partial class StencilOverlay
worldHandle.DrawRect(localAABB, Color.White);
}, Color.Transparent);
worldHandle.SetTransform(Matrix3.Identity);
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
var curTime = _timing.RealTime;

View File

@@ -10,7 +10,7 @@ public sealed partial class StencilOverlay
{
private List<Entity<MapGridComponent>> _grids = new();
private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha, Matrix3 invMatrix)
private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha, Matrix3x2 invMatrix)
{
var worldHandle = args.WorldHandle;
var mapId = args.MapId;
@@ -32,7 +32,7 @@ public sealed partial class StencilOverlay
foreach (var grid in _grids)
{
var matrix = _transform.GetWorldMatrix(grid, xformQuery);
Matrix3.Multiply(in matrix, in invMatrix, out var matty);
var matty = Matrix3x2.Multiply(matrix, invMatrix);
worldHandle.SetTransform(matty);
foreach (var tile in grid.Comp.GetTilesIntersecting(worldAABB))
@@ -52,7 +52,7 @@ public sealed partial class StencilOverlay
}, Color.Transparent);
worldHandle.SetTransform(Matrix3.Identity);
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
var curTime = _timing.RealTime;
@@ -62,7 +62,7 @@ public sealed partial class StencilOverlay
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, Vector2.Zero, modulate: (weatherProto.Color ?? Color.White).WithAlpha(alpha));
worldHandle.SetTransform(Matrix3.Identity);
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(null);
}
}

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Client.Parallax;
using Content.Client.Weather;
using Content.Shared.Salvage;
@@ -72,6 +73,6 @@ public sealed partial class StencilOverlay : Overlay
}
args.WorldHandle.UseShader(null);
args.WorldHandle.SetTransform(Matrix3.Identity);
args.WorldHandle.SetTransform(Matrix3x2.Identity);
}
}

View File

@@ -50,7 +50,7 @@ public sealed partial class StampLabel : Label
base.Draw(handle);
// Restore a sane transform+shader
handle.SetTransform(Matrix3.Identity);
handle.SetTransform(Matrix3x2.Identity);
handle.UseShader(null);
}
}

View File

@@ -53,7 +53,7 @@ public sealed partial class StampWidget : PanelContainer
base.Draw(handle);
// Restore a sane transform+shader
handle.SetTransform(Matrix3.Identity);
handle.SetTransform(Matrix3x2.Identity);
handle.UseShader(null);
}
}

View File

@@ -218,7 +218,7 @@ public partial class NavMapControl : MapGridControl
// Convert to a world position
var unscaledPosition = (localPosition - MidPointVector) / MinimapScale;
var worldPosition = _transformSystem.GetWorldMatrix(_xform).Transform(new Vector2(unscaledPosition.X, -unscaledPosition.Y) + offset);
var worldPosition = Vector2.Transform(new Vector2(unscaledPosition.X, -unscaledPosition.Y) + offset, _transformSystem.GetWorldMatrix(_xform));
// Find closest tracked entity in range
var closestEntity = NetEntity.Invalid;
@@ -401,7 +401,7 @@ public partial class NavMapControl : MapGridControl
if (mapPos.MapId != MapId.Nullspace)
{
var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
var position = Vector2.Transform(mapPos.Position, _transformSystem.GetInvWorldMatrix(_xform)) - offset;
position = ScalePosition(new Vector2(position.X, -position.Y));
handle.DrawCircle(position, float.Sqrt(MinimapScale) * 2f, value.Color);
@@ -422,7 +422,7 @@ public partial class NavMapControl : MapGridControl
if (mapPos.MapId != MapId.Nullspace)
{
var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
var position = Vector2.Transform(mapPos.Position, _transformSystem.GetInvWorldMatrix(_xform)) - offset;
position = ScalePosition(new Vector2(position.X, -position.Y));
var scalingCoefficient = MinmapScaleModifier * float.Sqrt(MinimapScale);

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Shared.Examine;
using Robust.Client.Graphics;
using Robust.Client.Player;
@@ -55,7 +56,7 @@ public sealed class PopupOverlay : Overlay
if (args.ViewportControl == null)
return;
args.DrawingHandle.SetTransform(Matrix3.Identity);
args.DrawingHandle.SetTransform(Matrix3x2.Identity);
args.DrawingHandle.UseShader(_shader);
var scale = _configManager.GetCVar(CVars.DisplayUIScale);
@@ -90,7 +91,7 @@ public sealed class PopupOverlay : Overlay
e => e == popup.InitialPos.EntityId || e == ourEntity, entMan: _entManager))
continue;
var pos = matrix.Transform(mapPos.Position);
var pos = Vector2.Transform(mapPos.Position, matrix);
_controller.DrawPopup(popup, worldHandle, pos, scale);
}
}

View File

@@ -102,7 +102,8 @@ public sealed partial class PowerMonitoringWindow
button.ToolTip = Loc.GetString(name);
// Update power value
button.PowerValue.Text = Loc.GetString("power-monitoring-window-value", ("value", entry.PowerValue));
// Don't use SI prefixes, just give the number in W, so that it is readily apparent which consumer is using a lot of power.
button.PowerValue.Text = Loc.GetString("power-monitoring-window-button-value", ("value", Math.Round(entry.PowerValue).ToString("N0")));
}
private void UpdateEntrySourcesOrLoads(BoxContainer masterContainer, BoxContainer currentContainer, PowerMonitoringConsoleEntry[]? entries, SpriteSpecifier.Texture icon)
@@ -480,6 +481,7 @@ public sealed class PowerMonitoringButton : Button
PowerValue = new Label()
{
HorizontalAlignment = HAlignment.Right,
Align = Label.AlignMode.Right,
SetWidth = 72f,
Margin = new Thickness(10, 0, 0, 0),
ClipText = true,

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Shared.Shuttles.Events;
using Content.Shared.Shuttles.Systems;
using Robust.Client.Graphics;
@@ -75,6 +76,6 @@ public sealed class EmergencyShuttleOverlay : Overlay
args.WorldHandle.SetTransform(xform.WorldMatrix);
args.WorldHandle.DrawRect(Position.Value, Color.Red.WithAlpha(100));
args.WorldHandle.SetTransform(Matrix3.Identity);
args.WorldHandle.SetTransform(Matrix3x2.Identity);
}
}

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Shuttles.Components;
using Robust.Client.AutoGenerated;
@@ -115,7 +116,7 @@ public partial class BaseShuttleControl : MapGridControl
}
}
protected void DrawGrid(DrawingHandleScreen handle, Matrix3 matrix, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
protected void DrawGrid(DrawingHandleScreen handle, Matrix3x2 matrix, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
{
var rator = Maps.GetAllTilesEnumerator(grid.Owner, grid.Comp);
var minimapScale = MinimapScale;
@@ -289,7 +290,7 @@ public partial class BaseShuttleControl : MapGridControl
public float MinimapScale;
public Vector2 MidPoint;
public Matrix3 Matrix;
public Matrix3x2 Matrix;
public List<Vector2> Vertices;
public Vector2[] ScaledVertices;
@@ -297,7 +298,7 @@ public partial class BaseShuttleControl : MapGridControl
public void Execute(int index)
{
var vert = Vertices[index];
var adjustedVert = Matrix.Transform(vert);
var adjustedVert = Vector2.Transform(vert, Matrix);
adjustedVert = adjustedVert with { Y = -adjustedVert.Y };
var scaledVert = ScalePosition(adjustedVert, MinimapScale, MidPoint);

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Shared.Shuttles.BUIStates;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
@@ -68,7 +69,7 @@ public sealed partial class NavScreen : BoxContainer
}
var (_, worldRot, worldMatrix) = _xformSystem.GetWorldPositionRotationMatrix(gridXform);
var worldPos = worldMatrix.Transform(gridBody.LocalCenter);
var worldPos = Vector2.Transform(gridBody.LocalCenter, worldMatrix);
// Get the positive reduced angle.
var displayRot = -worldRot.Reduced();

View File

@@ -108,10 +108,10 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
var gridNent = EntManager.GetNetEntity(GridEntity);
var mapPos = _xformSystem.ToMapCoordinates(_coordinates.Value);
var ourGridMatrix = _xformSystem.GetWorldMatrix(gridXform.Owner);
var dockMatrix = Matrix3.CreateTransform(_coordinates.Value.Position, Angle.Zero);
Matrix3.Multiply(dockMatrix, ourGridMatrix, out var offsetMatrix);
var dockMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, Angle.Zero);
var worldFromDock = Matrix3x2.Multiply(dockMatrix, ourGridMatrix);
offsetMatrix = offsetMatrix.Invert();
Matrix3x2.Invert(worldFromDock, out var offsetMatrix);
// Draw nearby grids
var controlBounds = PixelSizeBox;
@@ -137,7 +137,7 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
continue;
var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner);
Matrix3.Multiply(in gridMatrix, in offsetMatrix, out var matty);
var matty = Matrix3x2.Multiply(gridMatrix, offsetMatrix);
var color = _shuttles.GetIFFColor(grid.Owner, grid.Owner == GridEntity, component: iffComp);
DrawGrid(handle, matty, grid, color);
@@ -151,23 +151,23 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
if (ViewedDock == dock.Entity)
continue;
var position = matty.Transform(dock.Coordinates.Position);
var position = Vector2.Transform(dock.Coordinates.Position, matty);
var otherDockRotation = Matrix3.CreateRotation(dock.Angle);
var otherDockRotation = Matrix3Helpers.CreateRotation(dock.Angle);
var scaledPos = ScalePosition(position with {Y = -position.Y});
if (!controlBounds.Contains(scaledPos.Floored()))
continue;
// Draw the dock's collision
var collisionBL = matty.Transform(dock.Coordinates.Position +
otherDockRotation.Transform(new Vector2(-0.2f, -0.7f)));
var collisionBR = matty.Transform(dock.Coordinates.Position +
otherDockRotation.Transform(new Vector2(0.2f, -0.7f)));
var collisionTR = matty.Transform(dock.Coordinates.Position +
otherDockRotation.Transform(new Vector2(0.2f, -0.5f)));
var collisionTL = matty.Transform(dock.Coordinates.Position +
otherDockRotation.Transform(new Vector2(-0.2f, -0.5f)));
var collisionBL = Vector2.Transform(dock.Coordinates.Position +
Vector2.Transform(new Vector2(-0.2f, -0.7f), otherDockRotation), matty);
var collisionBR = Vector2.Transform(dock.Coordinates.Position +
Vector2.Transform(new Vector2(0.2f, -0.7f), otherDockRotation), matty);
var collisionTR = Vector2.Transform(dock.Coordinates.Position +
Vector2.Transform(new Vector2(0.2f, -0.5f), otherDockRotation), matty);
var collisionTL = Vector2.Transform(dock.Coordinates.Position +
Vector2.Transform(new Vector2(-0.2f, -0.5f), otherDockRotation), matty);
var verts = new[]
{
@@ -195,10 +195,10 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, verts, otherDockConnection);
// Draw the dock itself
var dockBL = matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f));
var dockBR = matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f));
var dockTR = matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f));
var dockTL = matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f));
var dockBL = Vector2.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f), matty);
var dockBR = Vector2.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f), matty);
var dockTR = Vector2.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f), matty);
var dockTL = Vector2.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f), matty);
verts = new[]
{
@@ -308,14 +308,14 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
// Draw the dock's collision
var invertedPosition = Vector2.Zero;
invertedPosition.Y = -invertedPosition.Y;
var rotation = Matrix3.CreateRotation(-_angle.Value + MathF.PI);
var rotation = Matrix3Helpers.CreateRotation(-_angle.Value + MathF.PI);
var ourDockConnection = new UIBox2(
ScalePosition(rotation.Transform(new Vector2(-0.2f, -0.7f))),
ScalePosition(rotation.Transform(new Vector2(0.2f, -0.5f))));
ScalePosition(Vector2.Transform(new Vector2(-0.2f, -0.7f), rotation)),
ScalePosition(Vector2.Transform(new Vector2(0.2f, -0.5f), rotation)));
var ourDock = new UIBox2(
ScalePosition(rotation.Transform(new Vector2(-0.5f, 0.5f))),
ScalePosition(rotation.Transform(new Vector2(0.5f, -0.5f))));
ScalePosition(Vector2.Transform(new Vector2(-0.5f, 0.5f), rotation)),
ScalePosition(Vector2.Transform(new Vector2(0.5f, -0.5f), rotation)));
var dockColor = Color.Magenta;
var connectionColor = Color.Pink;

View File

@@ -114,7 +114,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
var beaconsOnly = EntManager.TryGetComponent(mapUid, out FTLDestinationComponent? destComp) &&
destComp.BeaconsOnly;
var mapTransform = Matrix3.CreateInverseTransform(Offset, Angle.Zero);
var mapTransform = Matrix3Helpers.CreateInverseTransform(Offset, Angle.Zero);
if (beaconsOnly && TryGetBeacon(_beacons, mapTransform, args.RelativePixelPosition, PixelRect, out var foundBeacon, out _))
{
@@ -203,7 +203,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
/// </summary>
/// <param name="mapObjects"></param>
/// <returns></returns>
private List<IMapObject> GetViewportMapObjects(Matrix3 matty, List<IMapObject> mapObjects)
private List<IMapObject> GetViewportMapObjects(Matrix3x2 matty, List<IMapObject> mapObjects)
{
var results = new List<IMapObject>();
var enlargement = new Vector2i((int) (16 * UIScale), (int) (16 * UIScale));
@@ -217,7 +217,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
var mapCoords = _shuttles.GetMapCoordinates(mapObj);
var relativePos = matty.Transform(mapCoords.Position);
var relativePos = Vector2.Transform(mapCoords.Position, matty);
relativePos = relativePos with { Y = -relativePos.Y };
var uiPosition = ScalePosition(relativePos);
@@ -250,7 +250,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
DrawParallax(handle);
var viewedMapUid = _mapManager.GetMapEntityId(ViewingMap);
var matty = Matrix3.CreateInverseTransform(Offset, Angle.Zero);
var matty = Matrix3Helpers.CreateInverseTransform(Offset, Angle.Zero);
var realTime = _timing.RealTime;
var viewBox = new Box2(Offset - WorldRangeVector, Offset + WorldRangeVector);
var viewportObjects = GetViewportMapObjects(matty, mapObjects);
@@ -267,7 +267,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(shuttleXform);
gridPos = Maps.GetGridPosition((gridUid, gridPhysics), gridPos, gridRot);
var gridRelativePos = matty.Transform(gridPos);
var gridRelativePos = Vector2.Transform(gridPos, matty);
gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y };
var gridUiPos = ScalePosition(gridRelativePos);
@@ -296,7 +296,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
continue;
}
var adjustedPos = matty.Transform(mapCoords.Position);
var adjustedPos = Vector2.Transform(mapCoords.Position, matty);
var localPos = ScalePosition(adjustedPos with { Y = -adjustedPos.Y});
handle.DrawCircle(localPos, exclusion.Range * MinimapScale, exclusionColor.WithAlpha(0.05f));
handle.DrawCircle(localPos, exclusion.Range * MinimapScale, exclusionColor, filled: false);
@@ -319,7 +319,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
foreach (var (beaconName, coords, mapO) in GetBeacons(viewportObjects, matty, controlLocalBounds))
{
var localPos = matty.Transform(coords.Position);
var localPos = Vector2.Transform(coords.Position, matty);
localPos = localPos with { Y = -localPos.Y };
var beaconUiPos = ScalePosition(localPos);
var mapObject = GetMapObject(localPos, Angle.Zero, scale: 0.75f, scalePosition: true);
@@ -360,7 +360,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(grid.Owner);
gridPos = Maps.GetGridPosition((grid, gridPhysics), gridPos, gridRot);
var gridRelativePos = matty.Transform(gridPos);
var gridRelativePos = Vector2.Transform(gridPos, matty);
gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y };
var gridUiPos = ScalePosition(gridRelativePos);
@@ -439,7 +439,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
var color = ftlFree ? Color.LimeGreen : Color.Magenta;
var gridRelativePos = matty.Transform(gridPos);
var gridRelativePos = Vector2.Transform(gridPos, matty);
gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y };
var gridUiPos = ScalePosition(gridRelativePos);
@@ -512,7 +512,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
/// <summary>
/// Returns the beacons that intersect the viewport.
/// </summary>
private IEnumerable<(string Beacon, MapCoordinates Coordinates, IMapObject MapObject)> GetBeacons(List<IMapObject> mapObjs, Matrix3 mapTransform, UIBox2i area)
private IEnumerable<(string Beacon, MapCoordinates Coordinates, IMapObject MapObject)> GetBeacons(List<IMapObject> mapObjs, Matrix3x2 mapTransform, UIBox2i area)
{
foreach (var mapO in mapObjs)
{
@@ -520,7 +520,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
continue;
var beaconCoords = EntManager.GetCoordinates(beacon.Coordinates).ToMap(EntManager, _xformSystem);
var position = mapTransform.Transform(beaconCoords.Position);
var position = Vector2.Transform(beaconCoords.Position, mapTransform);
var localPos = ScalePosition(position with {Y = -position.Y});
// If beacon not on screen then ignore it.
@@ -557,7 +557,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
return mapObj;
}
private bool TryGetBeacon(IEnumerable<IMapObject> mapObjects, Matrix3 mapTransform, Vector2 mousePos, UIBox2i area, out ShuttleBeaconObject foundBeacon, out Vector2 foundLocalPos)
private bool TryGetBeacon(IEnumerable<IMapObject> mapObjects, Matrix3x2 mapTransform, Vector2 mousePos, UIBox2i area, out ShuttleBeaconObject foundBeacon, out Vector2 foundLocalPos)
{
// In pixels
const float BeaconSnapRange = 32f;
@@ -579,7 +579,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
if (!_shuttles.CanFTLBeacon(beaconObj.Coordinates))
continue;
var position = mapTransform.Transform(beaconCoords.Position);
var position = Vector2.Transform(beaconCoords.Position, mapTransform);
var localPos = ScalePosition(position with {Y = -position.Y});
// If beacon not on screen then ignore it.

View File

@@ -137,10 +137,10 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
var mapPos = _transform.ToMapCoordinates(_coordinates.Value);
var offset = _coordinates.Value.Position;
var posMatrix = Matrix3.CreateTransform(offset, _rotation.Value);
var posMatrix = Matrix3Helpers.CreateTransform(offset, _rotation.Value);
var (_, ourEntRot, ourEntMatrix) = _transform.GetWorldPositionRotationMatrix(_coordinates.Value.EntityId);
Matrix3.Multiply(posMatrix, ourEntMatrix, out var ourWorldMatrix);
var ourWorldMatrixInvert = ourWorldMatrix.Invert();
var ourWorldMatrix = Matrix3x2.Multiply(posMatrix, ourEntMatrix);
Matrix3x2.Invert(ourWorldMatrix, out var ourWorldMatrixInvert);
// Draw our grid in detail
var ourGridId = xform.GridUid;
@@ -148,7 +148,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
fixturesQuery.HasComponent(ourGridId.Value))
{
var ourGridMatrix = _transform.GetWorldMatrix(ourGridId.Value);
Matrix3.Multiply(in ourGridMatrix, in ourWorldMatrixInvert, out var matrix);
var matrix = Matrix3x2.Multiply(ourGridMatrix, ourWorldMatrixInvert);
var color = _shuttles.GetIFFColor(ourGridId.Value, self: true);
DrawGrid(handle, matrix, (ourGridId.Value, ourGrid), color);
@@ -194,7 +194,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
continue;
var gridMatrix = _transform.GetWorldMatrix(gUid);
Matrix3.Multiply(in gridMatrix, in ourWorldMatrixInvert, out var matty);
var matty = Matrix3x2.Multiply(gridMatrix, ourWorldMatrixInvert);
var color = _shuttles.GetIFFColor(grid, self: false, iff);
// Others default:
@@ -207,7 +207,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
{
var gridBounds = grid.Comp.LocalAABB;
var gridCentre = matty.Transform(gridBody.LocalCenter);
var gridCentre = Vector2.Transform(gridBody.LocalCenter, matty);
gridCentre.Y = -gridCentre.Y;
var distance = gridCentre.Length();
var labelText = Loc.GetString("shuttle-console-iff-label", ("name", labelName),
@@ -242,7 +242,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
}
}
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3 matrix)
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3x2 matrix)
{
if (!ShowDocks)
return;
@@ -255,7 +255,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
foreach (var state in docks)
{
var position = state.Coordinates.Position;
var uiPosition = matrix.Transform(position);
var uiPosition = Vector2.Transform(position, matrix);
if (uiPosition.Length() > (WorldRange * 2f) - DockScale)
continue;
@@ -264,10 +264,10 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
var verts = new[]
{
matrix.Transform(position + new Vector2(-DockScale, -DockScale)),
matrix.Transform(position + new Vector2(DockScale, -DockScale)),
matrix.Transform(position + new Vector2(DockScale, DockScale)),
matrix.Transform(position + new Vector2(-DockScale, DockScale)),
Vector2.Transform(position + new Vector2(-DockScale, -DockScale), matrix),
Vector2.Transform(position + new Vector2(DockScale, -DockScale), matrix),
Vector2.Transform(position + new Vector2(DockScale, DockScale), matrix),
Vector2.Transform(position + new Vector2(-DockScale, DockScale), matrix),
};
for (var i = 0; i < verts.Length; i++)

View File

@@ -39,8 +39,8 @@ public sealed class StatusIconOverlay : Overlay
var eyeRot = args.Viewport.Eye?.Rotation ?? default;
var xformQuery = _entity.GetEntityQuery<TransformComponent>();
var scaleMatrix = Matrix3.CreateScale(new Vector2(1, 1));
var rotationMatrix = Matrix3.CreateRotation(-eyeRot);
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(1, 1));
var rotationMatrix = Matrix3Helpers.CreateRotation(-eyeRot);
var query = _entity.AllEntityQueryEnumerator<StatusIconComponent, SpriteComponent, TransformComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out var comp, out var sprite, out var xform, out var meta))
@@ -59,9 +59,9 @@ public sealed class StatusIconOverlay : Overlay
if (icons.Count == 0)
continue;
var worldMatrix = Matrix3.CreateTranslation(worldPos);
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
var worldMatrix = Matrix3Helpers.CreateTranslation(worldPos);
var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix);
var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld);
handle.SetTransform(matty);
var countL = 0;

View File

@@ -1,4 +1,5 @@
using System.Linq;
using System.Linq;
using System.Numerics;
using Content.Client.Animations;
using Content.Shared.Hands;
using Content.Shared.Storage;
@@ -149,7 +150,7 @@ public sealed class StorageSystem : SharedStorageSystem
}
var finalMapPos = finalCoords.ToMapPos(EntityManager, TransformSystem);
var finalPos = TransformSystem.GetInvWorldMatrix(initialCoords.EntityId).Transform(finalMapPos);
var finalPos = Vector2.Transform(finalMapPos, TransformSystem.GetInvWorldMatrix(initialCoords.EntityId));
_entityPickupAnimation.AnimateEntityPickup(item, initialCoords, finalPos, initialAngle);
}

View File

@@ -65,7 +65,7 @@ public sealed class DirectionIcon : TextureRect
if (_rotation != null)
{
var offset = (-_rotation.Value).RotateVec(Size * UIScale / 2) - Size * UIScale / 2;
handle.SetTransform(Matrix3.CreateTransform(GlobalPixelPosition - offset, -_rotation.Value));
handle.SetTransform(Matrix3Helpers.CreateTransform(GlobalPixelPosition - offset, -_rotation.Value));
}
base.Draw(handle);

View File

@@ -169,7 +169,7 @@ public partial class MapGridControl : LayoutContainer
var inversePos = (value - MidPointVector) / MinimapScale;
inversePos = inversePos with { Y = -inversePos.Y };
inversePos = Matrix3.CreateTransform(Offset, Angle.Zero).Transform(inversePos);
inversePos = Vector2.Transform(inversePos, Matrix3Helpers.CreateTransform(Offset, Angle.Zero));
return inversePos;
}

View File

@@ -0,0 +1,285 @@
using System.Numerics;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.UserInterface.Controls;
// This control is not part of engine because I quickly wrote it in 2 hours at 2 AM and don't want to deal with
// API stabilization and/or figuring out relation to GridContainer.
// Grid layout is a complicated problem and I don't want to commit another half-baked thing into the engine.
// It's probably sufficient for its use case (RichTextLabel tables for rules/guidebook).
// Despite that, it's still better comment the shit half of you write on a regular basis.
//
// EMO: thank you PJB i was going to kill myself.
/// <summary>
/// Displays children in a tabular grid. Unlike <see cref="GridContainer"/>,
/// properly handles layout constraints so putting word-wrapping <see cref="RichTextLabel"/> in it should work.
/// </summary>
/// <remarks>
/// All children are automatically laid out in <see cref="Columns"/> columns.
/// The first control is in the top left, laid out per row from there.
/// </remarks>
[Virtual]
public class TableContainer : Container
{
private int _columns = 1;
/// <summary>
/// The absolute minimum width a column can be forced to.
/// </summary>
/// <remarks>
/// <para>
/// If a column *asks* for less width than this (small contents), it can still be smaller.
/// But if it asks for more it cannot go below this width.
/// </para>
/// </remarks>
public float MinForcedColumnWidth { get; set; } = 50;
// Scratch space used while calculating layout, cached to avoid regular allocations during layout pass.
private ColumnData[] _columnDataCache = [];
private RowData[] _rowDataCache = [];
/// <summary>
/// How many columns should be displayed.
/// </summary>
public int Columns
{
get => _columns;
set
{
ArgumentOutOfRangeException.ThrowIfLessThan(value, 1, nameof(value));
_columns = value;
}
}
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
ResetCachedArrays();
// Do a first pass measuring all child controls as if they're given infinite space.
// This gives us a maximum width the columns want, which we use to proportion them later.
var columnIdx = 0;
foreach (var child in Children)
{
ref var column = ref _columnDataCache[columnIdx];
child.Measure(new Vector2(float.PositiveInfinity, float.PositiveInfinity));
column.MaxWidth = Math.Max(column.MaxWidth, child.DesiredSize.X);
columnIdx += 1;
if (columnIdx == _columns)
columnIdx = 0;
}
// Calculate Slack and MinWidth for all columns. Also calculate sums for all columns.
var totalMinWidth = 0f;
var totalMaxWidth = 0f;
var totalSlack = 0f;
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
column.MinWidth = Math.Min(column.MaxWidth, MinForcedColumnWidth);
column.Slack = column.MaxWidth - column.MinWidth;
totalMinWidth += column.MinWidth;
totalMaxWidth += column.MaxWidth;
totalSlack += column.Slack;
}
if (totalMaxWidth <= availableSize.X)
{
// We want less horizontal space than we're given. Huh, that's convenient.
// Just set assigned width to be however much they asked for.
// We could probably skip the second measure pass in this scenario,
// but that's just an optimization, so I don't care right now.
//
// There's probably a very clever way to make this behavior work with the else block of logic,
// just by fiddling with the math.
// I'm dumb, it's 4:30 AM. Yeah, I *started* at 2 AM.
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
column.AssignedWidth = column.MaxWidth;
}
}
else
{
// We don't have enough horizontal space,
// at least without causing *some* sort of word wrapping (assuming text contents).
//
// Assign horizontal space proportional to the wanted maximum size of the columns.
var assignableWidth = Math.Max(0, availableSize.X - totalMinWidth);
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
var slackRatio = column.Slack / totalSlack;
column.AssignedWidth = column.MinWidth + slackRatio * assignableWidth;
}
}
// Go over controls for a second measuring pass, this time giving them their assigned measure width.
// This will give us a height to slot into per-row data.
// We still measure assuming infinite vertical space.
// This control can't properly handle being constrained on the Y axis.
columnIdx = 0;
var rowIdx = 0;
foreach (var child in Children)
{
ref var column = ref _columnDataCache[columnIdx];
ref var row = ref _rowDataCache[rowIdx];
child.Measure(new Vector2(column.AssignedWidth, float.PositiveInfinity));
row.MeasuredHeight = Math.Max(row.MeasuredHeight, child.DesiredSize.Y);
columnIdx += 1;
if (columnIdx == _columns)
{
columnIdx = 0;
rowIdx += 1;
}
}
// Sum up height of all rows to get final measured table height.
var totalHeight = 0f;
for (var r = 0; r < _rowDataCache.Length; r++)
{
ref var row = ref _rowDataCache[r];
totalHeight += row.MeasuredHeight;
}
return new Vector2(Math.Min(availableSize.X, totalMaxWidth), totalHeight);
}
protected override Vector2 ArrangeOverride(Vector2 finalSize)
{
// TODO: Expand to fit given vertical space.
// Calculate MinWidth and Slack sums again from column data.
// We could've cached these from measure but whatever.
var totalMinWidth = 0f;
var totalSlack = 0f;
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
totalMinWidth += column.MinWidth;
totalSlack += column.Slack;
}
// Calculate new width based on final given size, also assign horizontal positions of all columns.
var assignableWidth = Math.Max(0, finalSize.X - totalMinWidth);
var xPos = 0f;
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
var slackRatio = column.Slack / totalSlack;
column.ArrangedWidth = column.MinWidth + slackRatio * assignableWidth;
column.ArrangedX = xPos;
xPos += column.ArrangedWidth;
}
// Do actual arrangement row-by-row.
var arrangeY = 0f;
for (var r = 0; r < _rowDataCache.Length; r++)
{
ref var row = ref _rowDataCache[r];
for (var c = 0; c < _columns; c++)
{
ref var column = ref _columnDataCache[c];
var index = c + r * _columns;
if (index >= ChildCount) // Quit early if we don't actually fill out the row.
break;
var child = GetChild(c + r * _columns);
child.Arrange(UIBox2.FromDimensions(column.ArrangedX, arrangeY, column.ArrangedWidth, row.MeasuredHeight));
}
arrangeY += row.MeasuredHeight;
}
return finalSize with { Y = arrangeY };
}
/// <summary>
/// Ensure cached array space is allocated to correct size and is reset to a clean slate.
/// </summary>
private void ResetCachedArrays()
{
// 1-argument Array.Clear() is not currently available in sandbox (added in .NET 6).
if (_columnDataCache.Length != _columns)
_columnDataCache = new ColumnData[_columns];
Array.Clear(_columnDataCache, 0, _columnDataCache.Length);
var rowCount = ChildCount / _columns;
if (ChildCount % _columns != 0)
rowCount += 1;
if (rowCount != _rowDataCache.Length)
_rowDataCache = new RowData[rowCount];
Array.Clear(_rowDataCache, 0, _rowDataCache.Length);
}
/// <summary>
/// Per-column data used during layout.
/// </summary>
private struct ColumnData
{
// Measure data.
/// <summary>
/// The maximum width any control in this column wants, if given infinite space.
/// Maximum of all controls on the column.
/// </summary>
public float MaxWidth;
/// <summary>
/// The minimum width this column may be given.
/// This is either <see cref="MaxWidth"/> or <see cref="TableContainer.MinForcedColumnWidth"/>.
/// </summary>
public float MinWidth;
/// <summary>
/// Difference between max and min width; how much this column can expand from its minimum.
/// </summary>
public float Slack;
/// <summary>
/// How much horizontal space this column was assigned at measure time.
/// </summary>
public float AssignedWidth;
// Arrange data.
/// <summary>
/// How much horizontal space this column was assigned at arrange time.
/// </summary>
public float ArrangedWidth;
/// <summary>
/// The horizontal position this column was assigned at arrange time.
/// </summary>
public float ArrangedX;
}
private struct RowData
{
// Measure data.
/// <summary>
/// How much height the tallest control on this row was measured at,
/// measuring for infinite vertical space but assigned column width.
/// </summary>
public float MeasuredHeight;
}
}

View File

@@ -123,7 +123,6 @@ namespace Content.Client.Verbs
if ((visibility & MenuVisibility.Invisible) == 0)
{
var spriteQuery = GetEntityQuery<SpriteComponent>();
var tagQuery = GetEntityQuery<TagComponent>();
for (var i = entities.Count - 1; i >= 0; i--)
{
@@ -131,7 +130,7 @@ namespace Content.Client.Verbs
if (!spriteQuery.TryGetComponent(entity, out var spriteComponent) ||
!spriteComponent.Visible ||
_tagSystem.HasTag(entity, "HideContextMenu", tagQuery))
_tagSystem.HasTag(entity, "HideContextMenu"))
{
entities.RemoveSwap(i);
}

View File

@@ -277,8 +277,8 @@ namespace Content.Client.Viewport
EnsureViewportCreated();
var matrix = Matrix3.Invert(GetLocalToScreenMatrix());
coords = matrix.Transform(coords);
Matrix3x2.Invert(GetLocalToScreenMatrix(), out var matrix);
coords = Vector2.Transform(coords, matrix);
return _viewport!.LocalToWorld(coords);
}
@@ -291,8 +291,8 @@ namespace Content.Client.Viewport
EnsureViewportCreated();
var matrix = Matrix3.Invert(GetLocalToScreenMatrix());
coords = matrix.Transform(coords);
Matrix3x2.Invert(GetLocalToScreenMatrix(), out var matrix);
coords = Vector2.Transform(coords, matrix);
var ev = new PixelToMapEvent(coords, this, _viewport!);
_entityManager.EventBus.RaiseEvent(EventSource.Local, ref ev);
@@ -311,16 +311,16 @@ namespace Content.Client.Viewport
var matrix = GetLocalToScreenMatrix();
return matrix.Transform(vpLocal);
return Vector2.Transform(vpLocal, matrix);
}
public Matrix3 GetWorldToScreenMatrix()
public Matrix3x2 GetWorldToScreenMatrix()
{
EnsureViewportCreated();
return _viewport!.GetWorldToLocalMatrix() * GetLocalToScreenMatrix();
}
public Matrix3 GetLocalToScreenMatrix()
public Matrix3x2 GetLocalToScreenMatrix()
{
EnsureViewportCreated();
@@ -329,9 +329,9 @@ namespace Content.Client.Viewport
if (scaleFactor.X == 0 || scaleFactor.Y == 0)
// Basically a nonsense scenario, at least make sure to return something that can be inverted.
return Matrix3.Identity;
return Matrix3x2.Identity;
return Matrix3.CreateTransform(GlobalPixelPosition + drawBox.TopLeft, 0, scaleFactor);
return Matrix3Helpers.CreateTransform(GlobalPixelPosition + drawBox.TopLeft, 0, scaleFactor);
}
private void EnsureViewportCreated()

View File

@@ -81,7 +81,7 @@ public sealed partial class MeleeWeaponSystem
case WeaponArcAnimation.None:
var (mapPos, mapRot) = TransformSystem.GetWorldPositionRotation(userXform);
var worldPos = mapPos + (mapRot - userXform.LocalRotation).RotateVec(localPos);
var newLocalPos = TransformSystem.GetInvWorldMatrix(xform.ParentUid).Transform(worldPos);
var newLocalPos = Vector2.Transform(worldPos, TransformSystem.GetInvWorldMatrix(xform.ParentUid));
TransformSystem.SetLocalPositionNoLerp(animationUid, newLocalPos, xform);
if (arcComponent.Fadeout)
_animation.Play(animationUid, GetFadeAnimation(sprite, 0f, 0.15f), FadeAnimationKey);

View File

@@ -1,4 +1,4 @@
using System.Numerics;
using System.Numerics;
using Content.Client.Resources;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
@@ -53,7 +53,7 @@ public abstract class BaseBulletRenderer : Control
{
// Scale rendering in this control by UIScale.
var currentTransform = handle.GetTransform();
handle.SetTransform(Matrix3.CreateScale(new Vector2(UIScale)) * currentTransform);
handle.SetTransform(Matrix3Helpers.CreateScale(new Vector2(UIScale)) * currentTransform);
var countPerRow = CountPerRow(Size.X);

View File

@@ -19,6 +19,8 @@ namespace Content.IntegrationTests.Tests
[TestOf(typeof(EntityUid))]
public sealed class EntityTest
{
private static readonly ProtoId<EntityCategoryPrototype> SpawnerCategory = "Spawner";
[Test]
public async Task SpawnAndDeleteAllEntitiesOnDifferentMaps()
{
@@ -234,14 +236,6 @@ namespace Content.IntegrationTests.Tests
"StationEvent",
"TimedDespawn",
// Spawner entities
"DragonRift",
"RandomHumanoidSpawner",
"RandomSpawner",
"ConditionalSpawner",
"GhostRoleMobSpawner",
"NukeOperativeSpawner",
"TimedSpawner",
// makes an announcement on mapInit.
"AnnounceOnSpawn",
};
@@ -253,6 +247,7 @@ namespace Content.IntegrationTests.Tests
.Where(p => !p.Abstract)
.Where(p => !pair.IsTestPrototype(p))
.Where(p => !excluded.Any(p.Components.ContainsKey))
.Where(p => p.Categories.All(x => x.ID != SpawnerCategory))
.Select(p => p.ID)
.ToList();

View File

@@ -53,11 +53,13 @@ namespace Content.IntegrationTests.Tests.Tag
EntityUid sTagDummy = default;
TagComponent sTagComponent = null!;
Entity<TagComponent> sTagEntity = default;
await server.WaitPost(() =>
{
sTagDummy = sEntityManager.SpawnEntity(TagEntityId, MapCoordinates.Nullspace);
sTagComponent = sEntityManager.GetComponent<TagComponent>(sTagDummy);
sTagEntity = new Entity<TagComponent>(sTagDummy, sTagComponent);
});
await server.WaitAssertion(() =>
@@ -130,49 +132,64 @@ namespace Content.IntegrationTests.Tests.Tag
Assert.Multiple(() =>
{
// Cannot add the starting tag again
Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, StartingTag), Is.False);
Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, StartingTag), Is.False);
Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List<string> { StartingTag, StartingTag }), Is.False);
Assert.That(tagSystem.AddTag(sTagEntity, StartingTag), Is.False);
Assert.That(tagSystem.AddTags(sTagEntity, StartingTag, StartingTag), Is.False);
Assert.That(tagSystem.AddTags(sTagEntity, new List<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.False);
Assert.That(tagSystem.AddTags(sTagEntity, new HashSet<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.False);
// Has the starting tag
Assert.That(tagSystem.HasTag(sTagComponent, StartingTag), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, StartingTag), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<string> { StartingTag, StartingTag }), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, StartingTag), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<string> { StartingTag, StartingTag }), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.True);
// Does not have the added tag yet
Assert.That(tagSystem.HasTag(sTagComponent, AddedTag), Is.False);
Assert.That(tagSystem.HasAllTags(sTagComponent, AddedTag, AddedTag), Is.False);
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<string> { AddedTag, AddedTag }), Is.False);
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
Assert.That(tagSystem.HasAnyTag(sTagComponent, AddedTag, AddedTag), Is.False);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<string> { AddedTag, AddedTag }), Is.False);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
// Has a combination of the two tags
Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, AddedTag), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<string> { StartingTag, AddedTag }), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
// Does not have both tags
Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, AddedTag), Is.False);
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<string> { StartingTag, AddedTag }), Is.False);
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.False);
Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.False);
// Cannot remove a tag that does not exist
Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, AddedTag), Is.False);
Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.False);
Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List<string> { AddedTag, AddedTag }), Is.False);
Assert.That(tagSystem.RemoveTag(sTagEntity, AddedTag), Is.False);
Assert.That(tagSystem.RemoveTags(sTagEntity, AddedTag, AddedTag), Is.False);
Assert.That(tagSystem.RemoveTags(sTagEntity, new List<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
Assert.That(tagSystem.RemoveTags(sTagEntity, new HashSet<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
});
// Can add the new tag
Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.True);
Assert.That(tagSystem.AddTag(sTagEntity, AddedTag), Is.True);
Assert.Multiple(() =>
{
// Cannot add it twice
Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.False);
Assert.That(tagSystem.AddTag(sTagEntity, AddedTag), Is.False);
// Cannot add existing tags
Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, AddedTag), Is.False);
Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List<string> { StartingTag, AddedTag }), Is.False);
Assert.That(tagSystem.AddTags(sTagEntity, StartingTag, AddedTag), Is.False);
Assert.That(tagSystem.AddTags(sTagEntity, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.False);
Assert.That(tagSystem.AddTags(sTagEntity, new HashSet<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.False);
// Now has two tags
Assert.That(sTagComponent.Tags, Has.Count.EqualTo(2));
@@ -180,65 +197,103 @@ namespace Content.IntegrationTests.Tests.Tag
// Has both tags
Assert.That(tagSystem.HasTag(sTagComponent, StartingTag), Is.True);
Assert.That(tagSystem.HasTag(sTagComponent, AddedTag), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, StartingTag), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, AddedTag, StartingTag), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<string> { StartingTag, AddedTag }), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<string> { AddedTag, StartingTag }), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { AddedTag, StartingTag }), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { AddedTag, StartingTag }), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, AddedTag), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, AddedTag, StartingTag), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { AddedTag, StartingTag }), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { AddedTag, StartingTag }), Is.True);
});
Assert.Multiple(() =>
{
// Remove the existing starting tag
Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, StartingTag), Is.True);
Assert.That(tagSystem.RemoveTag(sTagEntity, StartingTag), Is.True);
// Remove the existing added tag
Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.True);
Assert.That(tagSystem.RemoveTags(sTagEntity, AddedTag, AddedTag), Is.True);
});
Assert.Multiple(() =>
{
// No tags left to remove
Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List<string> { StartingTag, AddedTag }), Is.False);
Assert.That(tagSystem.RemoveTags(sTagEntity, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.False);
// No tags left in the component
Assert.That(sTagComponent.Tags, Is.Empty);
});
#if !DEBUG
return;
// It is run only in DEBUG build,
// as the checks are performed only in DEBUG build.
#if DEBUG
// Has single
Assert.Throws<DebugAssertException>(() => { tagSystem.HasTag(sTagDummy, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasTag(sTagComponent, UnregisteredTag); });
// HasAny entityUid methods
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagDummy, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagDummy, UnregisteredTag, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagDummy, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagDummy, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
// HasAny component methods
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagComponent, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagComponent, UnregisteredTag, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
// HasAll entityUid methods
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagDummy, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagDummy, UnregisteredTag, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagDummy, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagDummy, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
// HasAll component methods
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagComponent, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagComponent, UnregisteredTag, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
// RemoveTag single
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTag(sTagDummy, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTag(sTagEntity, UnregisteredTag); });
// RemoveTags entityUid methods
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagDummy, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagDummy, UnregisteredTag, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagDummy, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagDummy, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
// RemoveTags entity methods
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagEntity, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagEntity, UnregisteredTag, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagEntity, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagEntity, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
// AddTag single
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTag(sTagDummy, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTag(sTagEntity, UnregisteredTag); });
// AddTags entityUid methods
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagDummy, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagDummy, UnregisteredTag, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagDummy, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagDummy, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
// AddTags entity methods
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagEntity, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagEntity, UnregisteredTag, UnregisteredTag); });
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagEntity, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagEntity, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
#endif
// Single
Assert.Throws<DebugAssertException>(() =>
{
tagSystem.HasTag(sTagDummy, UnregisteredTag);
});
Assert.Throws<DebugAssertException>(() =>
{
tagSystem.HasTag(sTagComponent, UnregisteredTag);
});
// Any
Assert.Throws<DebugAssertException>(() =>
{
tagSystem.HasAnyTag(sTagDummy, UnregisteredTag);
});
Assert.Throws<DebugAssertException>(() =>
{
tagSystem.HasAnyTag(sTagComponent, UnregisteredTag);
});
// All
Assert.Throws<DebugAssertException>(() =>
{
tagSystem.HasAllTags(sTagDummy, UnregisteredTag);
});
Assert.Throws<DebugAssertException>(() =>
{
tagSystem.HasAllTags(sTagComponent, UnregisteredTag);
});
});
await pair.CleanReturnAsync();
}

View File

@@ -1,6 +1,7 @@
using System.Linq;
using Content.Shared.Administration;
using Content.Shared.Tag;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
@@ -13,14 +14,14 @@ public sealed class TagCommand : ToolshedCommand
private TagSystem? _tag;
[CommandImplementation("list")]
public IEnumerable<string> List([PipedArgument] IEnumerable<EntityUid> ent)
public IEnumerable<ProtoId<TagPrototype>> List([PipedArgument] IEnumerable<EntityUid> ent)
{
return ent.SelectMany(x =>
{
if (TryComp<TagComponent>(x, out var tags))
// Note: Cast is required for C# to figure out the type signature.
return (IEnumerable<string>)tags.Tags;
return Array.Empty<string>();
return (IEnumerable<ProtoId<TagPrototype>>)tags.Tags;
return Array.Empty<ProtoId<TagPrototype>>();
});
}
@@ -72,7 +73,7 @@ public sealed class TagCommand : ToolshedCommand
)
{
_tag ??= GetSys<TagSystem>();
_tag.AddTags(input, @ref.Evaluate(ctx)!);
_tag.AddTags(input, (IEnumerable<ProtoId<TagPrototype>>)@ref.Evaluate(ctx)!);
return input;
}
@@ -92,7 +93,7 @@ public sealed class TagCommand : ToolshedCommand
)
{
_tag ??= GetSys<TagSystem>();
_tag.RemoveTags(input, @ref.Evaluate(ctx)!);
_tag.RemoveTags(input, (IEnumerable<ProtoId<TagPrototype>>)@ref.Evaluate(ctx)!);
return input;
}

View File

@@ -81,9 +81,7 @@ public partial class ChatSystem
bool ignoreActionBlocker = false
)
{
if (!(emote.Whitelist?.IsValid(source, EntityManager) ?? true))
return;
if (emote.Blacklist?.IsValid(source, EntityManager) ?? false)
if (_whitelistSystem.IsWhitelistFailOrNull(emote.Whitelist, source) || _whitelistSystem.IsBlacklistPass(emote.Blacklist, source))
return;
if (!emote.Available &&

View File

@@ -22,6 +22,7 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Players;
using Content.Shared.Radio;
using Content.Shared.Speech;
using Content.Shared.Whitelist;
using Robust.Server.Player;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
@@ -58,6 +59,7 @@ public sealed partial class ChatSystem : SharedChatSystem
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly ReplacementAccentSystem _wordreplacement = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
// Corvax-TTS-Start: Moved from Server to Shared
// public const int VoiceRange = 10; // how far voice goes in world units
@@ -827,7 +829,7 @@ public sealed partial class ChatSystem : SharedChatSystem
recipients.Add(player, new ICChatRecipientData(-1, true));
}
RaiseLocalEvent(new ExpandICChatRecipientstEvent(source, voiceGetRange, recipients));
RaiseLocalEvent(new ExpandICChatRecipientsEvent(source, voiceGetRange, recipients));
return recipients;
}
@@ -872,7 +874,7 @@ public sealed partial class ChatSystem : SharedChatSystem
/// This event is raised before chat messages are sent out to clients. This enables some systems to send the chat
/// messages to otherwise out-of view entities (e.g. for multiple viewports from cameras).
/// </summary>
public record ExpandICChatRecipientstEvent(EntityUid Source, float VoiceRange, Dictionary<ICommonSession, ChatSystem.ICChatRecipientData> Recipients)
public record ExpandICChatRecipientsEvent(EntityUid Source, float VoiceRange, Dictionary<ICommonSession, ChatSystem.ICChatRecipientData> Recipients)
{
}

View File

@@ -14,7 +14,7 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
public MapGridComponent Grid;
private bool _needToTransform = false;
private Matrix3 _matrix = Matrix3.Identity;
private Matrix3x2 _matrix = Matrix3x2.Identity;
private Vector2 _offset;
// Tiles which neighbor an exploding tile, but have not yet had the explosion spread to them due to an
@@ -44,7 +44,7 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
int typeIndex,
Dictionary<Vector2i, NeighborFlag> edgeTiles,
EntityUid? referenceGrid,
Matrix3 spaceMatrix,
Matrix3x2 spaceMatrix,
Angle spaceAngle)
{
Grid = grid;
@@ -72,9 +72,10 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
var transform = IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(Grid.Owner);
var size = (float) Grid.TileSize;
_matrix.R0C2 = size / 2;
_matrix.R1C2 = size / 2;
_matrix *= transform.WorldMatrix * Matrix3.Invert(spaceMatrix);
_matrix.M31 = size / 2;
_matrix.M32 = size / 2;
Matrix3x2.Invert(spaceMatrix, out var invSpace);
_matrix *= transform.WorldMatrix * invSpace;
var relativeAngle = transform.WorldRotation - spaceAngle;
_offset = relativeAngle.RotateVec(new Vector2(size / 4, size / 4));
}
@@ -228,7 +229,7 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
return;
}
var center = _matrix.Transform(tile);
var center = Vector2.Transform(tile, _matrix);
SpaceJump.Add(new((int) MathF.Floor(center.X + _offset.X), (int) MathF.Floor(center.Y + _offset.Y)));
SpaceJump.Add(new((int) MathF.Floor(center.X - _offset.Y), (int) MathF.Floor(center.Y + _offset.X)));
SpaceJump.Add(new((int) MathF.Floor(center.X - _offset.X), (int) MathF.Floor(center.Y - _offset.Y)));

View File

@@ -60,7 +60,7 @@ public sealed partial class ExplosionSystem : EntitySystem
{
Dictionary<Vector2i, BlockedSpaceTile> transformedEdges = new();
var targetMatrix = Matrix3.Identity;
var targetMatrix = Matrix3x2.Identity;
Angle targetAngle = new();
var tileSize = DefaultTileSize;
var maxDistanceSq = (int) (maxDistance * maxDistance);
@@ -75,9 +75,9 @@ public sealed partial class ExplosionSystem : EntitySystem
tileSize = targetGrid.TileSize;
}
var offsetMatrix = Matrix3.Identity;
offsetMatrix.R0C2 = tileSize / 2f;
offsetMatrix.R1C2 = tileSize / 2f;
var offsetMatrix = Matrix3x2.Identity;
offsetMatrix.M31 = tileSize / 2f;
offsetMatrix.M32 = tileSize / 2f;
// Here we can end up with a triple nested for loop:
// foreach other grid
@@ -106,7 +106,7 @@ public sealed partial class ExplosionSystem : EntitySystem
var xform = xforms.GetComponent(gridToTransform);
var (_, gridWorldRotation, gridWorldMatrix, invGridWorldMatrid) = xform.GetWorldPositionRotationMatrixWithInv(xforms);
var localEpicentre = (Vector2i) invGridWorldMatrid.Transform(epicentre.Position);
var localEpicentre = (Vector2i) Vector2.Transform(epicentre.Position, invGridWorldMatrid);
var matrix = offsetMatrix * gridWorldMatrix * targetMatrix;
var angle = gridWorldRotation - targetAngle;
@@ -119,7 +119,7 @@ public sealed partial class ExplosionSystem : EntitySystem
if (delta.X * delta.X + delta.Y * delta.Y > maxDistanceSq) // no Vector2.Length???
continue;
var center = matrix.Transform(tile);
var center = Vector2.Transform(tile, matrix);
if ((dir & NeighborFlag.Cardinal) == 0)
{

View File

@@ -298,8 +298,8 @@ public sealed partial class ExplosionSystem
/// Same as <see cref="ExplodeTile"/>, but for SPAAAAAAACE.
/// </summary>
internal void ExplodeSpace(BroadphaseComponent lookup,
Matrix3 spaceMatrix,
Matrix3 invSpaceMatrix,
Matrix3x2 spaceMatrix,
Matrix3x2 invSpaceMatrix,
Vector2i tile,
float throwForce,
DamageSpecifier damage,
@@ -341,7 +341,7 @@ public sealed partial class ExplosionSystem
}
private static bool SpaceQueryCallback(
ref (List<(EntityUid, TransformComponent)> List, HashSet<EntityUid> Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery<TransformComponent> XformQuery, Box2 GridBox, SharedTransformSystem System) state,
ref (List<(EntityUid, TransformComponent)> List, HashSet<EntityUid> Processed, Matrix3x2 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery<TransformComponent> XformQuery, Box2 GridBox, SharedTransformSystem System) state,
in EntityUid uid)
{
if (state.Processed.Contains(uid))
@@ -352,7 +352,7 @@ public sealed partial class ExplosionSystem
if (xform.ParentUid == state.LookupOwner)
{
// parented directly to the map, use local position
if (state.GridBox.Contains(state.InvSpaceMatrix.Transform(xform.LocalPosition)))
if (state.GridBox.Contains(Vector2.Transform(xform.LocalPosition, state.InvSpaceMatrix)))
state.List.Add((uid, xform));
return true;
@@ -360,14 +360,14 @@ public sealed partial class ExplosionSystem
// finally check if it intersects our tile
var wpos = state.System.GetWorldPosition(xform);
if (state.GridBox.Contains(state.InvSpaceMatrix.Transform(wpos)))
if (state.GridBox.Contains(Vector2.Transform(wpos, state.InvSpaceMatrix)))
state.List.Add((uid, xform));
return true;
}
private static bool SpaceQueryCallback(
ref (List<(EntityUid, TransformComponent)> List, HashSet<EntityUid> Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery<TransformComponent> XformQuery, Box2 GridBox, SharedTransformSystem System) state,
ref (List<(EntityUid, TransformComponent)> List, HashSet<EntityUid> Processed, Matrix3x2 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery<TransformComponent> XformQuery, Box2 GridBox, SharedTransformSystem System) state,
in FixtureProxy proxy)
{
var uid = proxy.Entity;
@@ -585,12 +585,12 @@ sealed class Explosion
/// <summary>
/// The matrix that defines the reference frame for the explosion in space.
/// </summary>
private readonly Matrix3 _spaceMatrix;
private readonly Matrix3x2 _spaceMatrix;
/// <summary>
/// Inverse of <see cref="_spaceMatrix"/>
/// </summary>
private readonly Matrix3 _invSpaceMatrix;
private readonly Matrix3x2 _invSpaceMatrix;
/// <summary>
/// Have all the tiles on all the grids been processed?
@@ -656,7 +656,7 @@ sealed class Explosion
List<ExplosionGridTileFlood> gridData,
List<float> tileSetIntensity,
MapCoordinates epicenter,
Matrix3 spaceMatrix,
Matrix3x2 spaceMatrix,
int area,
float tileBreakScale,
int maxTileBreak,
@@ -695,7 +695,7 @@ sealed class Explosion
});
_spaceMatrix = spaceMatrix;
_invSpaceMatrix = Matrix3.Invert(spaceMatrix);
Matrix3x2.Invert(spaceMatrix, out _invSpaceMatrix);
}
foreach (var grid in gridData)

View File

@@ -26,7 +26,7 @@ public sealed partial class ExplosionSystem : EntitySystem
/// <param name="maxIntensity">The maximum intensity that the explosion can have at any given tile. This
/// effectively caps the damage that this explosion can do.</param>
/// <returns>A list of tile-sets and a list of intensity values which describe the explosion.</returns>
private (int, List<float>, ExplosionSpaceTileFlood?, Dictionary<EntityUid, ExplosionGridTileFlood>, Matrix3)? GetExplosionTiles(
private (int, List<float>, ExplosionSpaceTileFlood?, Dictionary<EntityUid, ExplosionGridTileFlood>, Matrix3x2)? GetExplosionTiles(
MapCoordinates epicenter,
string typeID,
float totalIntensity,
@@ -84,7 +84,7 @@ public sealed partial class ExplosionSystem : EntitySystem
Dictionary<EntityUid, HashSet<Vector2i>>? previousGridJump;
// variables for transforming between grid and space-coordinates
var spaceMatrix = Matrix3.Identity;
var spaceMatrix = Matrix3x2.Identity;
var spaceAngle = Angle.Zero;
if (referenceGrid != null)
{

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Shared.Explosion;
using Content.Shared.Explosion.Components;
using Robust.Server.GameObjects;
@@ -35,7 +36,7 @@ public sealed partial class ExplosionSystem : EntitySystem
/// <summary>
/// Constructor for the shared <see cref="ExplosionEvent"/> using the server-exclusive explosion classes.
/// </summary>
private EntityUid CreateExplosionVisualEntity(MapCoordinates epicenter, string prototype, Matrix3 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable<ExplosionGridTileFlood> gridData, List<float> iterationIntensity)
private EntityUid CreateExplosionVisualEntity(MapCoordinates epicenter, string prototype, Matrix3x2 spaceMatrix, ExplosionSpaceTileFlood? spaceData, IEnumerable<ExplosionGridTileFlood> gridData, List<float> iterationIntensity)
{
var explosionEntity = Spawn(null, MapCoordinates.Nullspace);
var comp = AddComp<ExplosionVisualsComponent>(explosionEntity);

View File

@@ -3,7 +3,6 @@ using Robust.Shared.Prototypes;
namespace Content.Server.Flash.Components;
// Also needed FlashableComponent on entity to work
[RegisterComponent, Access(typeof(DamagedByFlashingSystem))]
public sealed partial class DamagedByFlashingComponent : Component
{
@@ -11,5 +10,5 @@ public sealed partial class DamagedByFlashingComponent : Component
/// damage from flashing
/// </summary>
[DataField(required: true), ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier FlashDamage = new ();
public DamageSpecifier FlashDamage = new();
}

View File

@@ -1,10 +1,13 @@
namespace Content.Server.Flash.Components
namespace Content.Server.Flash.Components;
/// <summary>
/// Makes the entity immune to being flashed.
/// When given to clothes in the "head", "eyes" or "mask" slot it protects the wearer.
/// </summary>
[RegisterComponent, Access(typeof(FlashSystem))]
public sealed partial class FlashImmunityComponent : Component
{
[RegisterComponent, Access(typeof(FlashSystem))]
public sealed partial class FlashImmunityComponent : Component
{
[ViewVariables(VVAccess.ReadWrite)]
[DataField("enabled")]
public bool Enabled { get; set; } = true;
}
[ViewVariables(VVAccess.ReadWrite)]
[DataField("enabled")]
public bool Enabled { get; set; } = true;
}

View File

@@ -9,18 +9,17 @@ using Content.Shared.Charges.Systems;
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Flash;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
using Content.Shared.Physics;
using Content.Shared.Tag;
using Content.Shared.Traits.Assorted;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.StatusEffect;
using Content.Shared.Examine;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using InventoryComponent = Content.Shared.Inventory.InventoryComponent;
namespace Content.Server.Flash
@@ -31,14 +30,14 @@ namespace Content.Server.Flash
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly SharedChargesSystem _charges = default!;
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
[Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
public override void Initialize()
{
@@ -46,7 +45,7 @@ namespace Content.Server.Flash
SubscribeLocalEvent<FlashComponent, MeleeHitEvent>(OnFlashMeleeHit);
// ran before toggling light for extra-bright lantern
SubscribeLocalEvent<FlashComponent, UseInHandEvent>(OnFlashUseInHand, before: new []{ typeof(HandheldLightSystem) });
SubscribeLocalEvent<FlashComponent, UseInHandEvent>(OnFlashUseInHand, before: new[] { typeof(HandheldLightSystem) });
SubscribeLocalEvent<InventoryComponent, FlashAttemptEvent>(OnInventoryFlashAttempt);
SubscribeLocalEvent<FlashImmunityComponent, FlashAttemptEvent>(OnFlashImmunityFlashAttempt);
SubscribeLocalEvent<PermanentBlindnessComponent, FlashAttemptEvent>(OnPermanentBlindnessFlashAttempt);
@@ -114,19 +113,35 @@ namespace Content.Server.Flash
float flashDuration,
float slowTo,
bool displayPopup = true,
FlashableComponent? flashable = null,
bool melee = false,
TimeSpan? stunDuration = null)
{
if (!Resolve(target, ref flashable, false))
return;
var attempt = new FlashAttemptEvent(target, user, used);
RaiseLocalEvent(target, attempt, true);
if (attempt.Cancelled)
return;
// don't paralyze, slowdown or convert to rev if the target is immune to flashes
if (!_statusEffectsSystem.TryAddStatusEffect<FlashedComponent>(target, FlashedKey, TimeSpan.FromSeconds(flashDuration / 1000f), true))
return;
if (stunDuration != null)
{
_stun.TryParalyze(target, stunDuration.Value, true);
}
else
{
_stun.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration / 1000f), true,
slowTo, slowTo);
}
if (displayPopup && user != null && target != user && Exists(user.Value))
{
_popup.PopupEntity(Loc.GetString("flash-component-user-blinds-you",
("user", Identity.Entity(user.Value, EntityManager))), target, target);
}
if (melee)
{
var ev = new AfterFlashedEvent(target, user, used);
@@ -135,48 +150,31 @@ namespace Content.Server.Flash
if (used != null)
RaiseLocalEvent(used.Value, ref ev);
}
flashable.LastFlash = _timing.CurTime;
flashable.Duration = flashDuration / 1000f; // TODO: Make this sane...
Dirty(target, flashable);
if (stunDuration != null)
{
_stun.TryParalyze(target, stunDuration.Value, true);
}
else
{
_stun.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration/1000f), true,
slowTo, slowTo);
}
if (displayPopup && user != null && target != user && Exists(user.Value))
{
_popup.PopupEntity(Loc.GetString("flash-component-user-blinds-you",
("user", Identity.Entity(user.Value, EntityManager))), target, target);
}
}
public void FlashArea(Entity<FlashComponent?> source, EntityUid? user, float range, float duration, float slowTo = 0.8f, bool displayPopup = false, float probability = 1f, SoundSpecifier? sound = null)
{
var transform = Transform(source);
var mapPosition = _transform.GetMapCoordinates(transform);
var flashableQuery = GetEntityQuery<FlashableComponent>();
var statusEffectsQuery = GetEntityQuery<StatusEffectsComponent>();
var damagedByFlashingQuery = GetEntityQuery<DamagedByFlashingComponent>();
foreach (var entity in _entityLookup.GetEntitiesInRange(transform.Coordinates, range))
{
if (!_random.Prob(probability))
continue;
if (!flashableQuery.TryGetComponent(entity, out var flashable))
// Is the entity affected by the flash either through status effects or by taking damage?
if (!statusEffectsQuery.HasComponent(entity) && !damagedByFlashingQuery.HasComponent(entity))
continue;
// Check for unobstructed entities while ignoring the mobs with flashable components.
if (!_interaction.InRangeUnobstructed(entity, mapPosition, range, flashable.CollisionGroup, predicate: (e) => flashableQuery.HasComponent(e) || e == source.Owner))
// Check for entites in view
// put damagedByFlashingComponent in the predicate because shadow anomalies block vision.
if (!_examine.InRangeUnOccluded(entity, mapPosition, range, predicate: (e) => damagedByFlashingQuery.HasComponent(e)))
continue;
// They shouldn't have flash removed in between right?
Flash(entity, user, source, duration, slowTo, displayPopup, flashableQuery.GetComponent(entity));
Flash(entity, user, source, duration, slowTo, displayPopup);
}
_audio.PlayPvs(sound, source, AudioParams.Default.WithVolume(1f).WithMaxDistance(3f));
@@ -195,13 +193,15 @@ namespace Content.Server.Flash
private void OnFlashImmunityFlashAttempt(EntityUid uid, FlashImmunityComponent component, FlashAttemptEvent args)
{
if(component.Enabled)
if (component.Enabled)
args.Cancel();
}
private void OnPermanentBlindnessFlashAttempt(EntityUid uid, PermanentBlindnessComponent component, FlashAttemptEvent args)
{
args.Cancel();
// check for total blindness
if (component.Blindness == 0)
args.Cancel();
}
private void OnTemporaryBlindnessFlashAttempt(EntityUid uid, TemporaryBlindnessComponent component, FlashAttemptEvent args)
@@ -210,6 +210,10 @@ namespace Content.Server.Flash
}
}
/// <summary>
/// Called before a flash is used to check if the attempt is cancelled by blindness, items or FlashImmunityComponent.
/// Raised on the target hit by the flash, the user of the flash and the flash used.
/// </summary>
public sealed class FlashAttemptEvent : CancellableEntityEventArgs
{
public readonly EntityUid Target;
@@ -224,8 +228,8 @@ namespace Content.Server.Flash
}
}
/// <summary>
/// Called after a flash is used via melee on another person to check for rev conversion.
/// Raised on the user of the flash, the target hit by the flash, and the flash used.
/// Called after a flash is used via melee on another person to check for rev conversion.
/// Raised on the target hit by the flash, the user of the flash and the flash used.
/// </summary>
[ByRefEvent]
public readonly struct AfterFlashedEvent
@@ -241,6 +245,4 @@ namespace Content.Server.Flash
Used = used;
}
}
}

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Chemistry.Components;
@@ -317,7 +318,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
var userXform = Transform(user);
var targetPos = _transform.GetWorldPosition(target);
var localPos = _transform.GetInvWorldMatrix(userXform).Transform(targetPos);
var localPos = Vector2.Transform(targetPos, _transform.GetInvWorldMatrix(userXform));
localPos = userXform.LocalRotation.RotateVec(localPos);
_melee.DoLunge(user, used, Angle.Zero, localPos, null, false);

View File

@@ -418,7 +418,7 @@ namespace Content.Server.GameTicking
{
var gridXform = Transform(gridUid);
return new EntityCoordinates(gridUid, gridXform.InvWorldMatrix.Transform(toMap.Position));
return new EntityCoordinates(gridUid, Vector2.Transform(toMap.Position, gridXform.InvWorldMatrix));
}
return spawn;

View File

@@ -1,3 +1,5 @@
using Robust.Shared.Prototypes;
namespace Content.Server.GameTicking.Rules.Components;
/// <summary>
@@ -5,6 +7,5 @@ namespace Content.Server.GameTicking.Rules.Components;
/// and providing loadout + name for the operative on spawn.
/// TODO: Remove once systems can request spawns from the ghost role system directly.
/// </summary>
[RegisterComponent]
[RegisterComponent, EntityCategory("Spawner")]
public sealed partial class NukeOperativeSpawnerComponent : Component;

View File

@@ -3,6 +3,7 @@ using Content.Server.Gatherable.Components;
using Content.Shared.Interaction;
using Content.Shared.Tag;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Whitelist;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
@@ -18,6 +19,7 @@ public sealed partial class GatherableSystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
public override void Initialize()
{
@@ -30,7 +32,7 @@ public sealed partial class GatherableSystem : EntitySystem
private void OnAttacked(Entity<GatherableComponent> gatherable, ref AttackedEvent args)
{
if (gatherable.Comp.ToolWhitelist?.IsValid(args.Used, EntityManager) != true)
if (_whitelistSystem.IsWhitelistFailOrNull(gatherable.Comp.ToolWhitelist, args.Used))
return;
Gather(gatherable, args.User);
@@ -41,7 +43,7 @@ public sealed partial class GatherableSystem : EntitySystem
if (args.Handled || !args.Complex)
return;
if (gatherable.Comp.ToolWhitelist?.IsValid(args.User, EntityManager) != true)
if (_whitelistSystem.IsWhitelistFailOrNull(gatherable.Comp.ToolWhitelist, args.User))
return;
Gather(gatherable, args.User);

View File

@@ -5,7 +5,7 @@ namespace Content.Server.Ghost.Roles.Components
/// <summary>
/// Allows a ghost to take this role, spawning a new entity.
/// </summary>
[RegisterComponent]
[RegisterComponent, EntityCategory("Spawner")]
[Access(typeof(GhostRoleSystem))]
public sealed partial class GhostRoleMobSpawnerComponent : Component
{

View File

@@ -8,7 +8,7 @@ namespace Content.Server.Humanoid.Components;
/// This is added to a marker entity in order to spawn a randomized
/// humanoid ingame.
/// </summary>
[RegisterComponent]
[RegisterComponent, EntityCategory("Spawner")]
public sealed partial class RandomHumanoidSpawnerComponent : Component
{
[DataField("settings", customTypeSerializer: typeof(PrototypeIdSerializer<RandomHumanoidSettingsPrototype>))]

View File

@@ -14,6 +14,7 @@ namespace Content.Server.Mech.Systems;
public sealed class MechAssemblySystem : EntitySystem
{
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly TagSystem _tag = default!;
/// <inheritdoc/>
public override void Initialize()
@@ -44,7 +45,7 @@ public sealed class MechAssemblySystem : EntitySystem
foreach (var (tag, val) in component.RequiredParts)
{
if (!val && tagComp.Tags.Contains(tag))
if (!val && _tag.HasTag(tagComp, tag))
{
component.RequiredParts[tag] = true;
_container.Insert(args.Used, component.PartsContainer);

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Server.Access.Systems;
using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components;
@@ -366,8 +367,8 @@ public sealed class SuitSensorSystem : EntitySystem
if (transform.GridUid != null)
{
coordinates = new EntityCoordinates(transform.GridUid.Value,
_transform.GetInvWorldMatrix(xformQuery.GetComponent(transform.GridUid.Value), xformQuery)
.Transform(_transform.GetWorldPosition(transform, xformQuery)));
Vector2.Transform(_transform.GetWorldPosition(transform, xformQuery),
_transform.GetInvWorldMatrix(xformQuery.GetComponent(transform.GridUid.Value), xformQuery)));
}
else if (transform.MapUid != null)
{

View File

@@ -36,7 +36,7 @@ public sealed partial class PathfindingSystem
return Vector2.Zero;
}
endPos = startXform.InvWorldMatrix.Transform(endXform.WorldMatrix.Transform(endPos));
endPos = Vector2.Transform(Vector2.Transform(endPos, endXform.WorldMatrix), startXform.InvWorldMatrix);
}
// TODO: Numerics when we changeover.

View File

@@ -395,7 +395,7 @@ public sealed partial class PathfindingSystem
private Vector2i GetOrigin(EntityCoordinates coordinates, EntityUid gridUid)
{
var localPos = _transform.GetInvWorldMatrix(gridUid).Transform(coordinates.ToMapPos(EntityManager, _transform));
var localPos = Vector2.Transform(coordinates.ToMapPos(EntityManager, _transform), _transform.GetInvWorldMatrix(gridUid));
return new Vector2i((int) Math.Floor(localPos.X / ChunkSize), (int) Math.Floor(localPos.Y / ChunkSize));
}

View File

@@ -405,7 +405,7 @@ namespace Content.Server.NPC.Pathfinding
return null;
}
var localPos = xform.InvWorldMatrix.Transform(coordinates.ToMapPos(EntityManager, _transform));
var localPos = Vector2.Transform(coordinates.ToMapPos(EntityManager, _transform), xform.InvWorldMatrix);
var origin = GetOrigin(localPos);
if (!TryGetChunk(origin, comp, out var chunk))

View File

@@ -1,6 +1,7 @@
using System.Numerics;
using Content.Shared.NPC.Components;
using Content.Shared.NPC.Systems;
using Content.Shared.Whitelist;
using Robust.Shared.Collections;
using Robust.Shared.Map;
using Robust.Shared.Random;
@@ -13,6 +14,7 @@ public sealed partial class NPCImprintingOnSpawnBehaviourSystem : SharedNPCImpri
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly NPCSystem _npc = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
public override void Initialize()
{
@@ -27,7 +29,7 @@ public sealed partial class NPCImprintingOnSpawnBehaviourSystem : SharedNPCImpri
foreach (var friend in friends)
{
if (imprinting.Comp.Whitelist?.IsValid(friend) != false)
if (_whitelistSystem.IsWhitelistPassOrNull(imprinting.Comp.Whitelist, friend))
{
AddImprintingTarget(imprinting, friend, imprinting.Comp);
}

View File

@@ -19,6 +19,7 @@ using Content.Shared.Tools.Systems;
using Content.Shared.Weapons.Melee;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Whitelist;
using Microsoft.Extensions.ObjectPool;
using Robust.Server.Containers;
using Robust.Shared.Prototypes;
@@ -46,6 +47,7 @@ public sealed class NPCUtilitySystem : EntitySystem
[Dependency] private readonly SolutionContainerSystem _solutions = default!;
[Dependency] private readonly WeldableSystem _weldable = default!;
[Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
private EntityQuery<PuddleComponent> _puddleQuery;
private EntityQuery<TransformComponent> _xformQuery;
@@ -249,7 +251,7 @@ public sealed class NPCUtilitySystem : EntitySystem
return 0f;
}
if (heldGun.Whitelist?.IsValid(targetUid, EntityManager) != true)
if (_whitelistSystem.IsWhitelistFailOrNull(heldGun.Whitelist, targetUid))
{
return 0f;
}

View File

@@ -10,6 +10,7 @@ using Robust.Shared.Containers;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Storage.Components;
using Robust.Server.Containers;
using Content.Shared.Whitelist;
namespace Content.Server.Power.EntitySystems;
@@ -20,6 +21,7 @@ internal sealed class ChargerSystem : EntitySystem
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly BatterySystem _battery = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
public override void Initialize()
{
@@ -208,7 +210,7 @@ internal sealed class ChargerSystem : EntitySystem
if (!receiverComponent.Powered)
return;
if (component.Whitelist?.IsValid(targetEntity, EntityManager) == false)
if (_whitelistSystem.IsWhitelistFail(component.Whitelist, targetEntity))
return;
if (!SearchForBattery(targetEntity, out var batteryUid, out var heldBattery))

View File

@@ -1,3 +1,4 @@
using System.Diagnostics;
using Robust.Shared.Utility;
using System.Linq;
using Robust.Shared.Threading;
@@ -37,6 +38,7 @@ namespace Content.Server.Power.Pow3r
DebugTools.Assert(state.GroupedNets.Select(x => x.Count).Sum() == state.Networks.Count);
_networkJob.State = state;
_networkJob.FrameTime = frameTime;
ValidateNetworkGroups(state, state.GroupedNets);
// Each network height layer can be run in parallel without issues.
foreach (var group in state.GroupedNets)
@@ -321,9 +323,69 @@ namespace Content.Server.Power.Pow3r
RecursivelyEstimateNetworkDepth(state, network, groupedNetworks);
}
ValidateNetworkGroups(state, groupedNetworks);
return groupedNetworks;
}
/// <summary>
/// Validate that network grouping is up to date. I.e., that it is safe to solve each networking in a given
/// group in parallel. This assumes that batteries are the only device that connects to multiple networks, and
/// is thus the only obstacle to solving everything in parallel.
/// </summary>
[Conditional("DEBUG")]
private void ValidateNetworkGroups(PowerState state, List<List<Network>> groupedNetworks)
{
HashSet<Network> nets = new();
HashSet<NodeId> netIds = new();
foreach (var layer in groupedNetworks)
{
nets.Clear();
netIds.Clear();
foreach (var net in layer)
{
foreach (var batteryId in net.BatteryLoads)
{
var battery = state.Batteries[batteryId];
if (battery.LinkedNetworkDischarging == default)
continue;
var subNet = state.Networks[battery.LinkedNetworkDischarging];
if (battery.LinkedNetworkDischarging == net.Id)
{
DebugTools.Assert(subNet == net);
continue;
}
DebugTools.Assert(!nets.Contains(subNet));
DebugTools.Assert(!netIds.Contains(subNet.Id));
DebugTools.Assert(subNet.Height < net.Height);
}
foreach (var batteryId in net.BatterySupplies)
{
var battery = state.Batteries[batteryId];
if (battery.LinkedNetworkCharging == default)
continue;
var parentNet = state.Networks[battery.LinkedNetworkCharging];
if (battery.LinkedNetworkCharging == net.Id)
{
DebugTools.Assert(parentNet == net);
continue;
}
DebugTools.Assert(!nets.Contains(parentNet));
DebugTools.Assert(!netIds.Contains(parentNet.Id));
DebugTools.Assert(parentNet.Height > net.Height);
}
DebugTools.Assert(nets.Add(net));
DebugTools.Assert(netIds.Add(net.Id));
}
}
}
private static void RecursivelyEstimateNetworkDepth(PowerState state, Network network, List<List<Network>> groupedNetworks)
{
network.Height = -2;

View File

@@ -13,6 +13,7 @@ using Robust.Shared.Collections;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
@@ -24,13 +25,15 @@ public sealed partial class DungeonJob
* Run after the main dungeon generation
*/
private static readonly ProtoId<TagPrototype> WallTag = "Wall";
private bool HasWall(MapGridComponent grid, Vector2i tile)
{
var anchored = _maps.GetAnchoredEntitiesEnumerator(_gridUid, _grid, tile);
while (anchored.MoveNext(out var uid))
{
if (_tagQuery.TryGetComponent(uid, out var tagComp) && tagComp.Tags.Contains("Wall"))
if (_tag.HasTag(uid.Value, WallTag))
return true;
}

View File

@@ -19,7 +19,7 @@ public sealed partial class DungeonJob
var gen = _prototype.Index<DungeonPresetPrototype>(preset);
var dungeonRotation = _dungeon.GetDungeonRotation(seed);
var dungeonTransform = Matrix3.CreateTransform(_position, dungeonRotation);
var dungeonTransform = Matrix3Helpers.CreateTransform(_position, dungeonRotation);
var roomPackProtos = new Dictionary<Vector2i, List<DungeonRoomPackPrototype>>();
foreach (var pack in _prototype.EnumeratePrototypes<DungeonRoomPackPrototype>())
@@ -69,7 +69,7 @@ public sealed partial class DungeonJob
var dungeon = new Dungeon();
var availablePacks = new List<DungeonRoomPackPrototype>();
var chosenPacks = new DungeonRoomPackPrototype?[gen.RoomPacks.Count];
var packTransforms = new Matrix3[gen.RoomPacks.Count];
var packTransforms = new Matrix3x2[gen.RoomPacks.Count];
var packRotations = new Angle[gen.RoomPacks.Count];
// Actually pick the room packs and rooms
@@ -97,7 +97,7 @@ public sealed partial class DungeonJob
// Iterate every pack
random.Shuffle(availablePacks);
Matrix3 packTransform = default!;
Matrix3x2 packTransform = default!;
var found = false;
DungeonRoomPackPrototype pack = default!;
@@ -128,7 +128,7 @@ public sealed partial class DungeonJob
var aRotation = dir.AsDir().ToAngle();
// Use this pack
packTransform = Matrix3.CreateTransform(bounds.Center, aRotation);
packTransform = Matrix3Helpers.CreateTransform(bounds.Center, aRotation);
packRotations[i] = aRotation;
pack = aPack;
break;
@@ -168,7 +168,7 @@ public sealed partial class DungeonJob
{
var roomDimensions = new Vector2i(roomSize.Width, roomSize.Height);
Angle roomRotation = Angle.Zero;
Matrix3 matty;
Matrix3x2 matty;
if (!roomProtos.TryGetValue(roomDimensions, out var roomProto))
{
@@ -176,13 +176,13 @@ public sealed partial class DungeonJob
if (!roomProtos.TryGetValue(roomDimensions, out roomProto))
{
Matrix3.Multiply(packTransform, dungeonTransform, out matty);
matty = Matrix3x2.Multiply(packTransform, dungeonTransform);
for (var x = roomSize.Left; x < roomSize.Right; x++)
{
for (var y = roomSize.Bottom; y < roomSize.Top; y++)
{
var index = matty.Transform(new Vector2(x, y) + grid.TileSizeHalfVector - packCenter).Floored();
var index = Vector2.Transform(new Vector2(x, y) + grid.TileSizeHalfVector - packCenter, matty).Floored();
tiles.Add((index, new Tile(_tileDefManager["FloorPlanetGrass"].TileId)));
}
}
@@ -209,10 +209,10 @@ public sealed partial class DungeonJob
roomRotation += Math.PI;
}
var roomTransform = Matrix3.CreateTransform(roomSize.Center - packCenter, roomRotation);
var roomTransform = Matrix3Helpers.CreateTransform(roomSize.Center - packCenter, roomRotation);
Matrix3.Multiply(roomTransform, packTransform, out matty);
Matrix3.Multiply(matty, dungeonTransform, out var dungeonMatty);
matty = Matrix3x2.Multiply(roomTransform, packTransform);
var dungeonMatty = Matrix3x2.Multiply(matty, dungeonTransform);
// The expensive bit yippy.
_dungeon.SpawnRoom(gridUid, grid, dungeonMatty, room);
@@ -232,7 +232,7 @@ public sealed partial class DungeonJob
continue;
}
var tilePos = dungeonMatty.Transform(new Vector2i(x + room.Offset.X, y + room.Offset.Y) + tileOffset);
var tilePos = Vector2.Transform(new Vector2i(x + room.Offset.X, y + room.Offset.Y) + tileOffset, dungeonMatty);
exterior.Add(tilePos.Floored());
}
}
@@ -244,7 +244,7 @@ public sealed partial class DungeonJob
for (var y = 0; y < room.Size.Y; y++)
{
var roomTile = new Vector2i(x + room.Offset.X, y + room.Offset.Y);
var tilePos = dungeonMatty.Transform(roomTile + tileOffset);
var tilePos = Vector2.Transform(roomTile + tileOffset, dungeonMatty);
var tileIndex = tilePos.Floored();
roomTiles.Add(tileIndex);

View File

@@ -28,10 +28,10 @@ public sealed partial class DungeonJob : Job<Dungeon>
private readonly DecalSystem _decals;
private readonly DungeonSystem _dungeon;
private readonly EntityLookupSystem _lookup;
private readonly TagSystem _tag;
private readonly TileSystem _tile;
private readonly SharedMapSystem _maps;
private readonly SharedTransformSystem _transform;
private EntityQuery<TagComponent> _tagQuery;
private readonly DungeonConfigPrototype _gen;
private readonly int _seed;
@@ -53,6 +53,7 @@ public sealed partial class DungeonJob : Job<Dungeon>
DecalSystem decals,
DungeonSystem dungeon,
EntityLookupSystem lookup,
TagSystem tag,
TileSystem tile,
SharedTransformSystem transform,
DungeonConfigPrototype gen,
@@ -72,10 +73,10 @@ public sealed partial class DungeonJob : Job<Dungeon>
_decals = decals;
_dungeon = dungeon;
_lookup = lookup;
_tag = tag;
_tile = tile;
_maps = _entManager.System<SharedMapSystem>();
_transform = transform;
_tagQuery = _entManager.GetEntityQuery<TagComponent>();
_gen = gen;
_grid = grid;

View File

@@ -67,7 +67,7 @@ public sealed partial class DungeonSystem
bool clearExisting = false,
bool rotation = false)
{
var originTransform = Matrix3.CreateTranslation(origin);
var originTransform = Matrix3Helpers.CreateTranslation(origin.X, origin.Y);
var roomRotation = Angle.Zero;
if (rotation)
@@ -75,8 +75,8 @@ public sealed partial class DungeonSystem
roomRotation = GetRoomRotation(room, random);
}
var roomTransform = Matrix3.CreateTransform((Vector2) room.Size / 2f, roomRotation);
Matrix3.Multiply(roomTransform, originTransform, out var finalTransform);
var roomTransform = Matrix3Helpers.CreateTransform((Vector2) room.Size / 2f, roomRotation);
var finalTransform = Matrix3x2.Multiply(roomTransform, originTransform);
SpawnRoom(gridUid, grid, finalTransform, room, clearExisting);
}
@@ -101,7 +101,7 @@ public sealed partial class DungeonSystem
public void SpawnRoom(
EntityUid gridUid,
MapGridComponent grid,
Matrix3 roomTransform,
Matrix3x2 roomTransform,
DungeonRoomPrototype room,
bool clearExisting = false)
{
@@ -116,7 +116,7 @@ public sealed partial class DungeonSystem
// go BRRNNTTT on existing stuff
if (clearExisting)
{
var gridBounds = new Box2(roomTransform.Transform(Vector2.Zero), roomTransform.Transform(room.Size));
var gridBounds = new Box2(Vector2.Transform(Vector2.Zero, roomTransform), Vector2.Transform(room.Size, roomTransform));
_entitySet.Clear();
// Polygon skin moment
gridBounds = gridBounds.Enlarged(-0.05f);
@@ -148,7 +148,7 @@ public sealed partial class DungeonSystem
var indices = new Vector2i(x + room.Offset.X, y + room.Offset.Y);
var tileRef = _maps.GetTileRef(templateMapUid, templateGrid, indices);
var tilePos = roomTransform.Transform(indices + tileOffset);
var tilePos = Vector2.Transform(indices + tileOffset, roomTransform);
var rounded = tilePos.Floored();
_tiles.Add((rounded, tileRef.Tile));
}
@@ -164,7 +164,7 @@ public sealed partial class DungeonSystem
foreach (var templateEnt in _lookup.GetEntitiesIntersecting(templateMapUid, bounds, LookupFlags.Uncontained))
{
var templateXform = _xformQuery.GetComponent(templateEnt);
var childPos = roomTransform.Transform(templateXform.LocalPosition - roomCenter);
var childPos = Vector2.Transform(templateXform.LocalPosition - roomCenter, roomTransform);
var childRot = templateXform.LocalRotation + finalRoomRotation;
var protoId = _metaQuery.GetComponent(templateEnt).EntityPrototype?.ID;
@@ -192,7 +192,7 @@ public sealed partial class DungeonSystem
// Offset by 0.5 because decals are offset from bot-left corner
// So we convert it to center of tile then convert it back again after transform.
// Do these shenanigans because 32x32 decals assume as they are centered on bottom-left of tiles.
var position = roomTransform.Transform(decal.Coordinates + Vector2Helpers.Half - roomCenter);
var position = Vector2.Transform(decal.Coordinates + Vector2Helpers.Half - roomCenter, roomTransform);
position -= Vector2Helpers.Half;
// Umm uhh I love decals so uhhhh idk what to do about this

View File

@@ -10,6 +10,7 @@ using Content.Shared.GameTicking;
using Content.Shared.Maps;
using Content.Shared.Physics;
using Content.Shared.Procedural;
using Content.Shared.Tag;
using Robust.Server.GameObjects;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
@@ -31,6 +32,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
[Dependency] private readonly AnchorableSystem _anchorable = default!;
[Dependency] private readonly DecalSystem _decals = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly TileSystem _tile = default!;
[Dependency] private readonly MapLoaderSystem _loader = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
@@ -199,6 +201,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
_decals,
this,
_lookup,
_tag,
_tile,
_transform,
gen,
@@ -231,6 +234,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
_decals,
this,
_lookup,
_tag,
_tile,
_transform,
gen,

View File

@@ -195,11 +195,11 @@ public partial class RadiationSystem
Vector2 srcLocal = sourceTrs.ParentUid == grid.Owner
? sourceTrs.LocalPosition
: gridTrs.InvLocalMatrix.Transform(ray.Source);
: Vector2.Transform(ray.Source, gridTrs.InvLocalMatrix);
Vector2 dstLocal = destTrs.ParentUid == grid.Owner
? destTrs.LocalPosition
: gridTrs.InvLocalMatrix.Transform(ray.Destination);
: Vector2.Transform(ray.Destination, gridTrs.InvLocalMatrix);
Vector2i sourceGrid = new(
(int) Math.Floor(srcLocal.X / grid.Comp.TileSize),

View File

@@ -28,6 +28,7 @@ using Content.Shared.Revenant.Components;
using Robust.Shared.Physics.Components;
using Robust.Shared.Utility;
using Robust.Shared.Map.Components;
using Content.Shared.Whitelist;
namespace Content.Server.Revenant.EntitySystems;
@@ -40,6 +41,7 @@ public sealed partial class RevenantSystem
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
[Dependency] private readonly GhostSystem _ghost = default!;
[Dependency] private readonly TileSystem _tile = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
private void InitializeAbilities()
{
@@ -246,7 +248,7 @@ public sealed partial class RevenantSystem
foreach (var ent in lookup)
{
//break windows
if (tags.HasComponent(ent) && _tag.HasAnyTag(ent, "Window"))
if (tags.HasComponent(ent) && _tag.HasTag(ent, "Window"))
{
//hardcoded damage specifiers til i die.
var dspec = new DamageSpecifier();
@@ -331,10 +333,8 @@ public sealed partial class RevenantSystem
foreach (var ent in _lookup.GetEntitiesInRange(uid, component.MalfunctionRadius))
{
if (component.MalfunctionWhitelist?.IsValid(ent, EntityManager) == false)
continue;
if (component.MalfunctionBlacklist?.IsValid(ent, EntityManager) == true)
if (_whitelistSystem.IsWhitelistFail(component.MalfunctionWhitelist, ent) ||
_whitelistSystem.IsBlacklistPass(component.MalfunctionBlacklist, ent))
continue;
_emag.DoEmagEffect(uid, ent); //it does not emag itself. adorable.

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Shared.Salvage.Fulton;
using Robust.Shared.Containers;
using Robust.Shared.Map;
@@ -61,8 +62,9 @@ public sealed class FultonSystem : SharedFultonSystem
var metadata = MetaData(uid);
var oldCoords = xform.Coordinates;
var offset = _random.NextVector2(1.5f);
var localPos = TransformSystem.GetInvWorldMatrix(beaconXform.ParentUid)
.Transform(TransformSystem.GetWorldPosition(beaconXform)) + offset;
var localPos = Vector2.Transform(
TransformSystem.GetWorldPosition(beaconXform),
TransformSystem.GetInvWorldMatrix(beaconXform.ParentUid)) + offset;
TransformSystem.SetCoordinates(uid, new EntityCoordinates(beaconXform.ParentUid, localPos));

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Robust.Shared.Map;
namespace Content.Server.Shuttles.Events;
@@ -6,4 +7,4 @@ namespace Content.Server.Shuttles.Events;
/// Raised when a shuttle has moved to FTL space.
/// </summary>
[ByRefEvent]
public readonly record struct FTLStartedEvent(EntityUid Entity, EntityCoordinates TargetCoordinates, EntityUid? FromMapUid, Matrix3 FTLFrom, Angle FromRotation);
public readonly record struct FTLStartedEvent(EntityUid Entity, EntityCoordinates TargetCoordinates, EntityUid? FromMapUid, Matrix3x2 FTLFrom, Angle FromRotation);

View File

@@ -1,4 +1,5 @@
using System.Linq;
using System.Numerics;
using Content.Server.Administration;
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems;
@@ -277,7 +278,7 @@ public sealed class ArrivalsSystem : EntitySystem
foreach (var (ent, xform) in toDump)
{
var rotation = xform.LocalRotation;
_transform.SetCoordinates(ent, new EntityCoordinates(args.FromMapUid!.Value, args.FTLFrom.Transform(xform.LocalPosition)));
_transform.SetCoordinates(ent, new EntityCoordinates(args.FromMapUid!.Value, Vector2.Transform(xform.LocalPosition, args.FTLFrom)));
_transform.SetWorldRotation(ent, args.FromRotation + rotation);
}
}

View File

@@ -46,13 +46,13 @@ public sealed partial class DockingSystem
FixturesComponent shuttleFixtures,
MapGridComponent grid,
bool isMap,
out Matrix3 matty,
out Matrix3x2 matty,
out Box2 shuttleDockedAABB,
out Angle gridRotation)
{
shuttleDockedAABB = Box2.UnitCentered;
gridRotation = Angle.Zero;
matty = Matrix3.Identity;
matty = Matrix3x2.Identity;
if (shuttleDock.Docked ||
gridDock.Docked ||
@@ -71,9 +71,9 @@ public sealed partial class DockingSystem
var gridDockAngle = gridDockXform.LocalRotation.Opposite();
var offsetAngle = gridDockAngle - shuttleDockAngle;
var stationDockMatrix = Matrix3.CreateInverseTransform(stationDockPos, shuttleDockAngle);
var gridXformMatrix = Matrix3.CreateTransform(gridDockXform.LocalPosition, gridDockAngle);
Matrix3.Multiply(in stationDockMatrix, in gridXformMatrix, out matty);
var stationDockMatrix = Matrix3Helpers.CreateInverseTransform(stationDockPos, shuttleDockAngle);
var gridXformMatrix = Matrix3Helpers.CreateTransform(gridDockXform.LocalPosition, gridDockAngle);
matty = Matrix3x2.Multiply(stationDockMatrix, gridXformMatrix);
if (!ValidSpawn(grid, matty, offsetAngle, shuttleFixtures, isMap))
return false;
@@ -193,7 +193,7 @@ public sealed partial class DockingSystem
}
// Can't just use the AABB as we want to get bounds as tight as possible.
var gridPosition = new EntityCoordinates(targetGrid, matty.Transform(Vector2.Zero));
var gridPosition = new EntityCoordinates(targetGrid, Vector2.Transform(Vector2.Zero, matty));
var spawnPosition = new EntityCoordinates(targetGridXform.MapUid!.Value, gridPosition.ToMapPos(EntityManager, _transform));
// TODO: use tight bounds
@@ -303,9 +303,9 @@ public sealed partial class DockingSystem
/// <summary>
/// Checks whether the shuttle can warp to the specified position.
/// </summary>
private bool ValidSpawn(MapGridComponent grid, Matrix3 matty, Angle angle, FixturesComponent shuttleFixturesComp, bool isMap)
private bool ValidSpawn(MapGridComponent grid, Matrix3x2 matty, Angle angle, FixturesComponent shuttleFixturesComp, bool isMap)
{
var transform = new Transform(matty.Transform(Vector2.Zero), angle);
var transform = new Transform(Vector2.Transform(Vector2.Zero, matty), angle);
// Because some docking bounds are tight af need to check each chunk individually
foreach (var fix in shuttleFixturesComp.Fixtures.Values)

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Server.Shuttles.Components;
using Content.Shared.Audio;
using Robust.Shared.Audio;
@@ -38,8 +39,8 @@ public sealed partial class ShuttleSystem
var otherXform = Transform(args.OtherEntity);
var ourPoint = ourXform.InvWorldMatrix.Transform(args.WorldPoint);
var otherPoint = otherXform.InvWorldMatrix.Transform(args.WorldPoint);
var ourPoint = Vector2.Transform(args.WorldPoint, ourXform.InvWorldMatrix);
var otherPoint = Vector2.Transform(args.WorldPoint, otherXform.InvWorldMatrix);
var ourVelocity = _physics.GetLinearVelocity(uid, ourPoint, ourBody, ourXform);
var otherVelocity = _physics.GetLinearVelocity(args.OtherEntity, otherPoint, otherBody, otherXform);

View File

@@ -267,7 +267,7 @@ public sealed partial class BorgSystem
return false;
}
if (component.ModuleWhitelist?.IsValid(module, EntityManager) == false)
if (_whitelistSystem.IsWhitelistFail(component.ModuleWhitelist, module))
{
if (user != null)
Popup.PopupEntity(Loc.GetString("borg-module-whitelist-deny"), uid, user.Value);

View File

@@ -22,6 +22,7 @@ using Content.Shared.Roles;
using Content.Shared.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components;
using Content.Shared.Throwing;
using Content.Shared.Whitelist;
using Content.Shared.Wires;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
@@ -53,6 +54,8 @@ public sealed partial class BorgSystem : SharedBorgSystem
[Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[ValidatePrototypeId<JobPrototype>]
public const string BorgJobId = "Borg";
@@ -104,9 +107,8 @@ public sealed partial class BorgSystem : SharedBorgSystem
return;
}
if (component.BrainEntity == null &&
brain != null &&
component.BrainWhitelist?.IsValid(used) != false)
if (component.BrainEntity == null && brain != null &&
_whitelistSystem.IsWhitelistPassOrNull(component.BrainWhitelist, used))
{
if (_mind.TryGetMind(used, out _, out var mind) && mind.Session != null)
{

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Server.Atmos.Components;
using Content.Server.Singularity.Components;
using Content.Shared.Ghost;
@@ -126,7 +127,7 @@ public sealed class GravityWellSystem : SharedGravityWellSystem
/// <param name="minRange">The minimum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="baseMatrixDeltaV">The base velocity added to any entities within affected by the gravity pulse scaled by the displacement of those entities from the epicenter.</param>
/// <param name="xform">(optional) The transform of the entity at the epicenter of the gravitational pulse.</param>
public void GravPulse(EntityUid uid, float maxRange, float minRange, in Matrix3 baseMatrixDeltaV, TransformComponent? xform = null)
public void GravPulse(EntityUid uid, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV, TransformComponent? xform = null)
{
if (Resolve(uid, ref xform))
GravPulse(xform.Coordinates, maxRange, minRange, in baseMatrixDeltaV);
@@ -154,7 +155,7 @@ public sealed class GravityWellSystem : SharedGravityWellSystem
/// <param name="maxRange">The maximum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="minRange">The minimum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="baseMatrixDeltaV">The base velocity added to any entities within affected by the gravity pulse scaled by the displacement of those entities from the epicenter.</param>
public void GravPulse(EntityCoordinates entityPos, float maxRange, float minRange, in Matrix3 baseMatrixDeltaV)
public void GravPulse(EntityCoordinates entityPos, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV)
=> GravPulse(entityPos.ToMap(EntityManager, _transform), maxRange, minRange, in baseMatrixDeltaV);
/// <summary>
@@ -175,7 +176,7 @@ public sealed class GravityWellSystem : SharedGravityWellSystem
/// <param name="maxRange">The maximum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="minRange">The minimum distance at which entities can be affected by the gravity pulse. Exists to prevent div/0 errors.</param>
/// <param name="baseMatrixDeltaV">The base velocity added to any entities within affected by the gravity pulse scaled by the displacement of those entities from the epicenter.</param>
public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange, in Matrix3 baseMatrixDeltaV)
public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV)
{
if (mapPos == MapCoordinates.Nullspace)
return; // No gravpulses in nullspace please.
@@ -205,7 +206,7 @@ public sealed class GravityWellSystem : SharedGravityWellSystem
continue;
var scaling = (1f / distance2) * physics.Mass; // TODO: Variable falloff gradiants.
_physics.ApplyLinearImpulse(entity, (displacement * baseMatrixDeltaV) * scaling, body: physics);
_physics.ApplyLinearImpulse(entity, Vector2.Transform(displacement, baseMatrixDeltaV) * scaling, body: physics);
}
}
@@ -218,10 +219,9 @@ public sealed class GravityWellSystem : SharedGravityWellSystem
/// <param name="baseRadialDeltaV">The base amount of velocity that will be added to entities in range towards the epicenter of the pulse.</param>
/// <param name="baseTangentialDeltaV">The base amount of velocity that will be added to entities in range counterclockwise relative to the epicenter of the pulse.</param>
public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange = 0.0f, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f)
=> GravPulse(mapPos, maxRange, minRange, new Matrix3(
baseRadialDeltaV, +baseTangentialDeltaV, 0.0f,
-baseTangentialDeltaV, baseRadialDeltaV, 0.0f,
0.0f, 0.0f, 1.0f
=> GravPulse(mapPos, maxRange, minRange, new Matrix3x2(
baseRadialDeltaV, -baseTangentialDeltaV, 0.0f,
+baseTangentialDeltaV, baseRadialDeltaV, 0.0f
));
#endregion GravPulse

View File

@@ -2,7 +2,7 @@ using Robust.Shared.Prototypes;
namespace Content.Server.Spawners.Components
{
[RegisterComponent]
[RegisterComponent, EntityCategory("Spawner")]
[Virtual]
public partial class ConditionalSpawnerComponent : Component
{

View File

@@ -2,7 +2,7 @@ using Robust.Shared.Prototypes;
namespace Content.Server.Spawners.Components
{
[RegisterComponent]
[RegisterComponent, EntityCategory("Spawner")]
public sealed partial class RandomSpawnerComponent : ConditionalSpawnerComponent
{
[ViewVariables(VVAccess.ReadWrite)]

View File

@@ -9,7 +9,7 @@ namespace Content.Server.Spawners.Components;
/// Can configure the set of entities, spawn timing, spawn chance,
/// and min/max number of entities to spawn.
/// </summary>
[RegisterComponent]
[RegisterComponent, EntityCategory("Spawner")]
public sealed partial class TimedSpawnerComponent : Component, ISerializationHooks
{
/// <summary>

View File

@@ -21,6 +21,7 @@ public sealed class SpreaderSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly SharedMapSystem _map = default!;
[Dependency] private readonly TagSystem _tag = default!;
/// <summary>
/// Cached maximum number of updates per spreader prototype. This is applied per-grid.
@@ -37,8 +38,7 @@ public sealed class SpreaderSystem : EntitySystem
public const float SpreadCooldownSeconds = 1;
[ValidatePrototypeId<TagPrototype>]
private const string IgnoredTag = "SpreaderIgnore";
private static readonly ProtoId<TagPrototype> IgnoredTag = "SpreaderIgnore";
/// <inheritdoc/>
public override void Initialize()
@@ -189,7 +189,6 @@ public sealed class SpreaderSystem : EntitySystem
var airtightQuery = GetEntityQuery<AirtightComponent>();
var dockQuery = GetEntityQuery<DockingComponent>();
var xformQuery = GetEntityQuery<TransformComponent>();
var tagQuery = GetEntityQuery<TagComponent>();
var blockedAtmosDirs = AtmosDirection.Invalid;
// Due to docking ports they may not necessarily be opposite directions.
@@ -212,7 +211,7 @@ public sealed class SpreaderSystem : EntitySystem
// If we're on a blocked tile work out which directions we can go.
if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked ||
tagQuery.TryGetComponent(ent, out var tags) && tags.Tags.Contains(IgnoredTag))
_tag.HasTag(ent.Value, IgnoredTag))
{
continue;
}
@@ -250,8 +249,7 @@ public sealed class SpreaderSystem : EntitySystem
while (directionEnumerator.MoveNext(out var ent))
{
if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked ||
tagQuery.TryGetComponent(ent, out var tags) && tags.Tags.Contains(IgnoredTag))
if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked || _tag.HasTag(ent.Value, IgnoredTag))
{
continue;
}

View File

@@ -24,6 +24,12 @@ public sealed partial class StationEventComponent : Component
[DataField]
public string? EndAnnouncement;
[DataField]
public Color StartAnnouncementColor = Color.Gold;
[DataField]
public Color EndAnnouncementColor = Color.Gold;
[DataField]
public SoundSpecifier? StartAudio;
@@ -42,12 +48,6 @@ public sealed partial class StationEventComponent : Component
[DataField]
public int ReoccurrenceDelay = 30;
/// <summary>
/// How long after being added does the event start
/// </summary>
[DataField]
public TimeSpan StartDelay = TimeSpan.Zero;
/// <summary>
/// How long the event lasts.
/// </summary>
@@ -75,13 +75,6 @@ public sealed partial class StationEventComponent : Component
[DataField]
public int? MaxOccurrences;
/// <summary>
/// When the station event starts.
/// </summary>
[DataField("startTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
public TimeSpan StartTime;
/// <summary>
/// When the station event ends.
/// </summary>

View File

@@ -1,6 +1,5 @@
using Content.Server.Anomaly;
using Content.Server.GameTicking.Components;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Station.Components;
using Content.Server.StationEvents.Components;
@@ -12,11 +11,14 @@ public sealed class AnomalySpawnRule : StationEventSystem<AnomalySpawnRuleCompon
protected override void Added(EntityUid uid, AnomalySpawnRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
{
base.Added(uid, component, gameRule, args);
if (!TryComp<StationEventComponent>(uid, out var stationEvent))
return;
var str = Loc.GetString("anomaly-spawn-event-announcement",
("sighting", Loc.GetString($"anomaly-spawn-sighting-{RobustRandom.Next(1, 6)}")));
ChatSystem.DispatchGlobalAnnouncement(str, colorOverride: Color.FromHex("#18abf5"));
stationEvent.StartAnnouncement = str;
base.Added(uid, component, gameRule, args);
}
protected override void Started(EntityUid uid, AnomalySpawnRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)

View File

@@ -1,5 +1,4 @@
using Content.Server.GameTicking.Components;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.StationEvents.Components;
using Robust.Shared.Random;
@@ -9,11 +8,14 @@ public sealed class BluespaceArtifactRule : StationEventSystem<BluespaceArtifact
{
protected override void Added(EntityUid uid, BluespaceArtifactRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
{
base.Added(uid, component, gameRule, args);
if (!TryComp<StationEventComponent>(uid, out var stationEvent))
return;
var str = Loc.GetString("bluespace-artifact-event-announcement",
("sighting", Loc.GetString(RobustRandom.Pick(component.PossibleSighting))));
ChatSystem.DispatchGlobalAnnouncement(str, colorOverride: Color.FromHex("#18abf5"));
stationEvent.StartAnnouncement = str;
base.Added(uid, component, gameRule, args);
}
protected override void Started(EntityUid uid, BluespaceArtifactRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)

View File

@@ -1,5 +1,4 @@
using Content.Server.GameTicking.Components;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Station.Components;
@@ -15,10 +14,14 @@ public sealed class BreakerFlipRule : StationEventSystem<BreakerFlipRuleComponen
protected override void Added(EntityUid uid, BreakerFlipRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
{
base.Added(uid, component, gameRule, args);
if (!TryComp<StationEventComponent>(uid, out var stationEvent))
return;
var str = Loc.GetString("station-event-breaker-flip-announcement", ("data", Loc.GetString(Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}"))));
ChatSystem.DispatchGlobalAnnouncement(str, playSound: false, colorOverride: Color.Gold);
stationEvent.StartAnnouncement = str;
base.Added(uid, component, gameRule, args);
}
protected override void Started(EntityUid uid, BreakerFlipRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)

View File

@@ -3,7 +3,6 @@ using Content.Server.Cargo.Components;
using Content.Server.Cargo.Systems;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Components;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Station.Components;
using Content.Server.StationEvents.Components;
using Robust.Shared.Prototypes;
@@ -18,11 +17,14 @@ public sealed class CargoGiftsRule : StationEventSystem<CargoGiftsRuleComponent>
protected override void Added(EntityUid uid, CargoGiftsRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
{
base.Added(uid, component, gameRule, args);
if (!TryComp<StationEventComponent>(uid, out var stationEvent))
return;
var str = Loc.GetString(component.Announce,
("sender", Loc.GetString(component.Sender)), ("description", Loc.GetString(component.Description)), ("dest", Loc.GetString(component.Dest)));
ChatSystem.DispatchGlobalAnnouncement(str, colorOverride: Color.FromHex("#18abf5"));
stationEvent.StartAnnouncement = str;
base.Added(uid, component, gameRule, args);
}
/// <summary>

View File

@@ -1,9 +1,7 @@
using System.Linq;
using Content.Server.GameTicking.Components;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.StationEvents.Components;
using JetBrains.Annotations;
using Robust.Shared.Player;
using Robust.Shared.Random;
namespace Content.Server.StationEvents.Events;
@@ -15,15 +13,16 @@ public sealed class FalseAlarmRule : StationEventSystem<FalseAlarmRuleComponent>
protected override void Started(EntityUid uid, FalseAlarmRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
if (!TryComp<StationEventComponent>(uid, out var stationEvent))
return;
var allEv = _event.AllEvents().Select(p => p.Value).ToList();
var picked = RobustRandom.Pick(allEv);
if (picked.StartAnnouncement != null)
{
ChatSystem.DispatchGlobalAnnouncement(Loc.GetString(picked.StartAnnouncement), playSound: false, colorOverride: Color.Gold);
}
Audio.PlayGlobal(picked.StartAudio, Filter.Broadcast(), true);
stationEvent.StartAnnouncement = picked.StartAnnouncement;
stationEvent.StartAudio = picked.StartAudio;
stationEvent.StartAnnouncementColor = picked.StartAnnouncementColor;
base.Started(uid, component, gameRule, args);
}
}

Some files were not shown because too many files have changed in this diff Show More