Merge remote-tracking branch 'wizards/master' into upstream-sync

This commit is contained in:
faint
2023-07-23 14:29:20 +03:00
85 changed files with 1342 additions and 1302 deletions

View File

@@ -11,14 +11,16 @@ public sealed class AccessOverlay : Overlay
{
private readonly IEntityManager _entityManager;
private readonly EntityLookupSystem _lookup;
private readonly SharedTransformSystem _xform;
private readonly Font _font;
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
public AccessOverlay(IEntityManager entManager, IResourceCache cache, EntityLookupSystem lookup)
public AccessOverlay(IEntityManager entManager, IResourceCache cache, EntityLookupSystem lookup, SharedTransformSystem xform)
{
_entityManager = entManager;
_lookup = lookup;
_xform = xform;
_font = cache.GetFont("/Fonts/NotoSans/NotoSans-Regular.ttf", 12);
}
@@ -71,7 +73,7 @@ public sealed class AccessOverlay : Overlay
textStr = "";
}
var screenPos = args.ViewportControl.WorldToScreen(xform.WorldPosition);
var screenPos = args.ViewportControl.WorldToScreen(_xform.GetWorldPosition(xform));
args.ScreenHandle.DrawString(_font, screenPos, textStr, Color.Gold);
}

View File

@@ -2,4 +2,6 @@ using Content.Shared.Access.Systems;
namespace Content.Client.Access;
public sealed class AccessSystem : SharedAccessSystem {}
public sealed class AccessSystem : SharedAccessSystem
{
}

View File

@@ -13,7 +13,8 @@ public sealed class ShowAccessReadersCommand : IConsoleCommand
{
var collection = IoCManager.Instance;
if (collection == null) return;
if (collection == null)
return;
var overlay = collection.Resolve<IOverlayManager>();
@@ -25,9 +26,10 @@ public sealed class ShowAccessReadersCommand : IConsoleCommand
var entManager = collection.Resolve<IEntityManager>();
var cache = collection.Resolve<IResourceCache>();
var system = entManager.EntitySysManager.GetEntitySystem<EntityLookupSystem>();
var lookup = entManager.System<EntityLookupSystem>();
var xform = entManager.System<SharedTransformSystem>();
overlay.AddOverlay(new AccessOverlay(entManager, cache, system));
overlay.AddOverlay(new AccessOverlay(entManager, cache, lookup, xform));
shell.WriteLine($"Set access reader debug overlay to true");
}
}

View File

@@ -56,9 +56,10 @@ namespace Content.Client.Access.UI
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
if (!disposing)
return;
_window?.Dispose();
}
}
}

View File

@@ -51,7 +51,9 @@ namespace Content.Client.Access.UI
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
if (!disposing)
return;
_window?.Dispose();
}

View File

@@ -15,6 +15,8 @@ namespace Content.Client.Access.UI
public sealed partial class IdCardConsoleWindow : DefaultWindow
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ILogManager _logManager = default!;
private readonly ISawmill _logMill = default!;
private readonly IdCardConsoleBoundUserInterface _owner;
@@ -30,6 +32,7 @@ namespace Content.Client.Access.UI
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_logMill = _logManager.GetSawmill(SharedIdCardConsoleSystem.Sawmill);
_owner = owner;
@@ -67,7 +70,7 @@ namespace Content.Client.Access.UI
{
if (!prototypeManager.TryIndex<AccessLevelPrototype>(access, out var accessLevel))
{
Logger.ErrorS(SharedIdCardConsoleSystem.Sawmill, $"Unable to find accesslevel for {access}");
_logMill.Error($"Unable to find accesslevel for {access}");
continue;
}

View File

@@ -1,4 +1,4 @@
using Content.Shared.Administration.Notes;
using Content.Shared.Administration.Notes;
using Content.Shared.Database;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
@@ -18,6 +18,7 @@ public sealed partial class AdminNotesLinePopup : Popup
public AdminNotesLinePopup(SharedAdminNote note, string playerName, bool showDelete, bool showEdit)
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
NoteId = note.Id;

View File

@@ -163,7 +163,7 @@ public sealed partial class NoteEdit : FancyWindow
return;
if (DeleteResetOn is null)
{
DeleteResetOn = _gameTiming.CurTime.Add(TimeSpan.FromSeconds(3));
DeleteResetOn = _gameTiming.RealTime + TimeSpan.FromSeconds(3);
SubmitButton.Text = Loc.GetString("admin-note-editor-submit-confirm");
SubmitButton.ModulateSelfOverride = Color.Red;
// Task.Delay(3000).ContinueWith(_ => ResetSubmitButton()); // TODO: fix
@@ -185,7 +185,7 @@ public sealed partial class NoteEdit : FancyWindow
{
base.FrameUpdate(args);
// This checks for null for free, do not invert it as null always produces a false value
if (DeleteResetOn > _gameTiming.CurTime)
if (DeleteResetOn < _gameTiming.RealTime)
{
ResetSubmitButton();
DeleteResetOn = null;

View File

@@ -40,6 +40,7 @@ namespace Content.Client.Administration.UI.Tabs.AdminbusTab
private void Reset()
{
var entManager = IoCManager.Resolve<IEntityManager>();
var xformSystem = entManager.System<SharedTransformSystem>();
var playerManager = IoCManager.Resolve<IPlayerManager>();
var player = playerManager.LocalPlayer?.ControlledEntity;
@@ -50,16 +51,16 @@ namespace Content.Client.Administration.UI.Tabs.AdminbusTab
if (entManager.TryGetComponent<TransformComponent>(player, out var xform))
{
currentMap = xform.MapID;
position = xform.WorldPosition;
position = xformSystem.GetWorldPosition(xform);
if (entManager.TryGetComponent<TransformComponent>(xform.GridUid, out var gridXform))
{
rotation = gridXform.WorldRotation;
rotation = xformSystem.GetWorldRotation(gridXform);
}
else
{
// MapId moment
rotation = xform.WorldRotation - xform.LocalRotation;
rotation = xformSystem.GetWorldRotation(xform) - xform.LocalRotation;
}
}

View File

@@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.Linq;
using Content.Client.Atmos.EntitySystems;
using Content.Shared.Atmos.Prototypes;
@@ -8,9 +7,6 @@ using Robust.Client.Console;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
namespace Content.Client.Administration.UI.Tabs.AtmosTab
@@ -19,24 +15,31 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
[UsedImplicitly]
public sealed partial class AddGasWindow : DefaultWindow
{
private IEnumerable<MapGridComponent>? _gridData;
private List<EntityUid>? _gridData;
private IEnumerable<GasPrototype>? _gasData;
protected override void EnteredTree()
{
// Fill out grids
_gridData = IoCManager.Resolve<IMapManager>().GetAllGrids().Where(g => (int) g.Owner != 0);
foreach (var grid in _gridData)
var entManager = IoCManager.Resolve<IEntityManager>();
var playerManager = IoCManager.Resolve<IPlayerManager>();
var gridQuery = entManager.AllEntityQueryEnumerator<MapGridComponent>();
_gridData ??= new List<EntityUid>();
_gridData.Clear();
while (gridQuery.MoveNext(out var uid, out _))
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridUid;
GridOptions.AddItem($"{grid.Owner} {(playerGrid == grid.Owner ? " (Current)" : "")}");
var player = playerManager.LocalPlayer?.ControlledEntity;
var playerGrid = entManager.GetComponentOrNull<TransformComponent>(player)?.GridUid;
GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
}
GridOptions.OnItemSelected += eventArgs => GridOptions.SelectId(eventArgs.Id);
// Fill out gases
_gasData = EntitySystem.Get<AtmosphereSystem>().Gases;
_gasData = entManager.System<AtmosphereSystem>().Gases;
foreach (var gas in _gasData)
{
var gasName = Loc.GetString(gas.Name);
@@ -53,8 +56,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
if (_gridData == null || _gasData == null)
return;
var gridList = _gridData.ToList();
var gridIndex = gridList[GridOptions.SelectedId].Owner;
var gridIndex = _gridData[GridOptions.SelectedId];
var gasList = _gasData.ToList();
var gasId = gasList[GasOptions.SelectedId].ID;

View File

@@ -19,24 +19,32 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
[UsedImplicitly]
public sealed partial class FillGasWindow : DefaultWindow
{
private IEnumerable<MapGridComponent>? _gridData;
private List<EntityUid>? _gridData;
private IEnumerable<GasPrototype>? _gasData;
protected override void EnteredTree()
{
// Fill out grids
_gridData = IoCManager.Resolve<IMapManager>().GetAllGrids().Where(g => (int) g.Owner != 0);
foreach (var grid in _gridData)
var entManager = IoCManager.Resolve<IEntityManager>();
var playerManager = IoCManager.Resolve<IPlayerManager>();
var gridQuery = entManager.AllEntityQueryEnumerator<MapGridComponent>();
_gridData ??= new List<EntityUid>();
_gridData.Clear();
while (gridQuery.MoveNext(out var uid, out _))
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridUid;
GridOptions.AddItem($"{grid.Owner} {(playerGrid == grid.Owner ? " (Current)" : "")}");
var player = playerManager.LocalPlayer?.ControlledEntity;
var playerGrid = entManager.GetComponentOrNull<TransformComponent>(player)?.GridUid;
GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
_gridData.Add(uid);
}
GridOptions.OnItemSelected += eventArgs => GridOptions.SelectId(eventArgs.Id);
// Fill out gases
_gasData = EntitySystem.Get<AtmosphereSystem>().Gases;
_gasData = entManager.System<AtmosphereSystem>().Gases;
foreach (var gas in _gasData)
{
var gasName = Loc.GetString(gas.Name);
@@ -53,8 +61,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
if (_gridData == null || _gasData == null)
return;
var gridList = _gridData.ToList();
var gridIndex = gridList[GridOptions.SelectedId].Owner;
var gridIndex = _gridData[GridOptions.SelectedId];
var gasList = _gasData.ToList();
var gasId = gasList[GasOptions.SelectedId].ID;

View File

@@ -17,16 +17,23 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
[UsedImplicitly]
public sealed partial class SetTemperatureWindow : DefaultWindow
{
private IEnumerable<MapGridComponent>? _data;
private List<EntityUid>? _data;
protected override void EnteredTree()
{
_data = IoCManager.Resolve<IMapManager>().GetAllGrids().Where(g => (int) g.Owner != 0);
foreach (var grid in _data)
var entManager = IoCManager.Resolve<IEntityManager>();
var playerManager = IoCManager.Resolve<IPlayerManager>();
var gridQuery = entManager.AllEntityQueryEnumerator<MapGridComponent>();
_data ??= new List<EntityUid>();
_data.Clear();
while (gridQuery.MoveNext(out var uid, out _))
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity;
var playerGrid = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<TransformComponent>(player)?.GridUid;
GridOptions.AddItem($"{grid.Owner} {(playerGrid == grid.Owner ? " (Current)" : "")}");
var player = playerManager.LocalPlayer?.ControlledEntity;
var playerGrid = entManager.GetComponentOrNull<TransformComponent>(player)?.GridUid;
GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
_data.Add(uid);
}
GridOptions.OnItemSelected += eventArgs => GridOptions.SelectId(eventArgs.Id);
@@ -37,8 +44,8 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
{
if (_data == null)
return;
var dataList = _data.ToList();
var selectedGrid = dataList[GridOptions.SelectedId].Owner;
var selectedGrid = _data[GridOptions.SelectedId];
IoCManager.Resolve<IClientConsoleHost>()
.ExecuteCommand($"settemp {TileXSpin.Value} {TileYSpin.Value} {selectedGrid} {TemperatureSpin.Value}");
}

View File

@@ -9,9 +9,9 @@ namespace Content.Client.Audio;
/// </summary>
public sealed class AmbientSoundOverlay : Overlay
{
private IEntityManager _entManager;
private AmbientSoundSystem _ambient;
private EntityLookupSystem _lookup;
private readonly IEntityManager _entManager;
private readonly AmbientSoundSystem _ambient;
private readonly EntityLookupSystem _lookup;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
@@ -27,6 +27,7 @@ public sealed class AmbientSoundOverlay : Overlay
var worldHandle = args.WorldHandle;
var ambientQuery = _entManager.GetEntityQuery<AmbientSoundComponent>();
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
var xformSystem = _entManager.System<SharedTransformSystem>();
const float Size = 0.25f;
const float Alpha = 0.25f;
@@ -40,16 +41,16 @@ public sealed class AmbientSoundOverlay : Overlay
{
if (_ambient.IsActive(ambientSound))
{
worldHandle.DrawCircle(xform.WorldPosition, Size, Color.LightGreen.WithAlpha(Alpha * 2f));
worldHandle.DrawCircle(xformSystem.GetWorldPosition(xform), Size, Color.LightGreen.WithAlpha(Alpha * 2f));
}
else
{
worldHandle.DrawCircle(xform.WorldPosition, Size, Color.Orange.WithAlpha(Alpha));
worldHandle.DrawCircle(xformSystem.GetWorldPosition(xform), Size, Color.Orange.WithAlpha(Alpha));
}
}
else
{
worldHandle.DrawCircle(xform.WorldPosition, Size, Color.Red.WithAlpha(Alpha));
worldHandle.DrawCircle(xformSystem.GetWorldPosition(xform), Size, Color.Red.WithAlpha(Alpha));
}
}
}

View File

@@ -32,7 +32,7 @@ namespace Content.Client.Inventory
public Action<SlotData>? EntitySlotUpdate = null;
public Action<SlotData>? OnSlotAdded = null;
public Action<SlotData>? OnSlotRemoved = null;
public Action<InventorySlotsComponent>? OnLinkInventorySlots = null;
public Action<EntityUid, InventorySlotsComponent>? OnLinkInventorySlots = null;
public Action? OnUnlinkInventory = null;
public Action<SlotSpriteUpdate>? OnSpriteUpdate = null;
@@ -138,7 +138,7 @@ namespace Content.Client.Inventory
}
}
OnLinkInventorySlots?.Invoke(component);
OnLinkInventorySlots?.Invoke(uid, component);
}
public override void Shutdown()
@@ -173,7 +173,7 @@ namespace Content.Client.Inventory
}
OnUnlinkInventory?.Invoke();
OnLinkInventorySlots?.Invoke(component);
OnLinkInventorySlots?.Invoke(player.Value, component);
}
public void SetSlotHighlight(EntityUid owner, InventorySlotsComponent component, string slotName, bool state)

View File

@@ -384,7 +384,7 @@ namespace Content.Client.Light.Components
public readonly List<LightBehaviourAnimationTrack> Behaviours = new();
[ViewVariables(VVAccess.ReadOnly)]
private readonly List<AnimationContainer> _animations = new();
public readonly List<AnimationContainer> Animations = new();
[ViewVariables(VVAccess.ReadOnly)]
private Dictionary<string, object> _originalPropertyValues = new();
@@ -400,60 +400,11 @@ namespace Content.Client.Light.Components
AnimationTracks = {behaviour}
};
_animations.Add(new AnimationContainer(key, animation, behaviour));
Animations.Add(new AnimationContainer(key, animation, behaviour));
key++;
}
}
protected override void Startup()
{
base.Startup();
// TODO: Do NOT ensure component here. And use eventbus events instead...
Owner.EnsureComponent<AnimationPlayerComponent>();
if (_entMan.TryGetComponent(Owner, out AnimationPlayerComponent? animation))
{
#pragma warning disable 618
animation.AnimationCompleted += OnAnimationCompleted;
#pragma warning restore 618
}
foreach (var container in _animations)
{
container.LightBehaviour.Initialize(Owner, _random, _entMan);
}
// we need to initialize all behaviours before starting any
foreach (var container in _animations)
{
if (container.LightBehaviour.Enabled)
{
StartLightBehaviour(container.LightBehaviour.ID);
}
}
}
private void OnAnimationCompleted(string key)
{
var container = _animations.FirstOrDefault(x => x.FullKey == key);
if (container == null)
{
return;
}
if (container.LightBehaviour.IsLooped)
{
container.LightBehaviour.UpdatePlaybackValues(container.Animation);
if (_entMan.TryGetComponent(Owner, out AnimationPlayerComponent? animation))
{
animation.Play(container.Animation, container.FullKey);
}
}
}
/// <summary>
/// If we disable all the light behaviours we want to be able to revert the light to its original state.
/// </summary>
@@ -485,7 +436,7 @@ namespace Content.Client.Light.Components
return;
}
foreach (var container in _animations)
foreach (var container in Animations)
{
if (container.LightBehaviour.ID == id || id == string.Empty)
{
@@ -516,7 +467,7 @@ namespace Content.Client.Light.Components
var toRemove = new List<AnimationContainer>();
foreach (var container in _animations)
foreach (var container in Animations)
{
if (container.LightBehaviour.ID == id || id == string.Empty)
{
@@ -534,7 +485,7 @@ namespace Content.Client.Light.Components
foreach (var container in toRemove)
{
_animations.Remove(container);
Animations.Remove(container);
}
if (resetToOriginalSettings && _entMan.TryGetComponent(Owner, out PointLightComponent? light))
@@ -559,7 +510,7 @@ namespace Content.Client.Light.Components
return false;
}
return _animations.Any(container => animation.HasRunningAnimation(KeyPrefix + container.Key));
return Animations.Any(container => animation.HasRunningAnimation(KeyPrefix + container.Key));
}
/// <summary>
@@ -569,7 +520,7 @@ namespace Content.Client.Light.Components
{
var key = 0;
while (_animations.Any(x => x.Key == key))
while (Animations.Any(x => x.Key == key))
{
key++;
}
@@ -582,7 +533,7 @@ namespace Content.Client.Light.Components
behaviour.Initialize(Owner, _random, _entMan);
var container = new AnimationContainer(key, animation, behaviour);
_animations.Add(container);
Animations.Add(container);
if (playImmediately)
{

View File

@@ -0,0 +1,55 @@
using System.Linq;
using Content.Client.Light.Components;
using Robust.Client.GameObjects;
using Robust.Shared.Random;
namespace Content.Client.Light.EntitySystems;
public sealed class LightBehaviorSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly AnimationPlayerSystem _player = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LightBehaviourComponent, ComponentStartup>(OnLightStartup);
SubscribeLocalEvent<LightBehaviourComponent, AnimationCompletedEvent>(OnBehaviorAnimationCompleted);
}
private void OnBehaviorAnimationCompleted(EntityUid uid, LightBehaviourComponent component, AnimationCompletedEvent args)
{
var container = component.Animations.FirstOrDefault(x => x.FullKey == args.Key);
if (container == null)
{
return;
}
if (container.LightBehaviour.IsLooped)
{
container.LightBehaviour.UpdatePlaybackValues(container.Animation);
_player.Play(uid, container.Animation, container.FullKey);
}
}
private void OnLightStartup(EntityUid uid, LightBehaviourComponent component, ComponentStartup args)
{
// TODO: Do NOT ensure component here. And use eventbus events instead...
EnsureComp<AnimationPlayerComponent>(uid);
foreach (var container in component.Animations)
{
container.LightBehaviour.Initialize(uid, _random, EntityManager);
}
// we need to initialize all behaviours before starting any
foreach (var container in component.Animations)
{
if (container.LightBehaviour.Enabled)
{
component.StartLightBehaviour(container.LightBehaviour.ID);
}
}
}
}

View File

@@ -14,7 +14,7 @@ public sealed class JetpackSystem : SharedJetpackSystem
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly ClothingSystem _clothing = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize()
{
@@ -22,7 +22,7 @@ public sealed class JetpackSystem : SharedJetpackSystem
SubscribeLocalEvent<JetpackComponent, AppearanceChangeEvent>(OnJetpackAppearance);
}
protected override bool CanEnable(JetpackComponent component)
protected override bool CanEnable(EntityUid uid, JetpackComponent component)
{
// No predicted atmos so you'd have to do a lot of funny to get this working.
return false;
@@ -30,7 +30,7 @@ public sealed class JetpackSystem : SharedJetpackSystem
private void OnJetpackAppearance(EntityUid uid, JetpackComponent component, ref AppearanceChangeEvent args)
{
_appearance.TryGetData<bool>(uid, JetpackVisuals.Enabled, out var enabled, args.Component);
Appearance.TryGetData<bool>(uid, JetpackVisuals.Enabled, out var enabled, args.Component);
var state = "icon" + (enabled ? "-on" : "");
args.Sprite?.LayerSetState(0, state);
@@ -43,16 +43,21 @@ public sealed class JetpackSystem : SharedJetpackSystem
{
base.Update(frameTime);
if (!_timing.IsFirstTimePredicted) return;
if (!_timing.IsFirstTimePredicted)
return;
foreach (var comp in EntityQuery<ActiveJetpackComponent>())
// TODO: Please don't copy-paste this I beg
// make a generic particle emitter system / actual particles instead.
var query = EntityQueryEnumerator<ActiveJetpackComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (_timing.CurTime < comp.TargetTime)
continue;
comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown);
CreateParticles(comp.Owner);
CreateParticles(uid);
}
}
@@ -62,7 +67,9 @@ public sealed class JetpackSystem : SharedJetpackSystem
if (Container.TryGetContainingContainer(uid, out var container) &&
TryComp<PhysicsComponent>(container.Owner, out var body) &&
body.LinearVelocity.LengthSquared() < 1f)
{
return;
}
var uidXform = Transform(uid);
var coordinates = uidXform.Coordinates;
@@ -70,19 +77,17 @@ public sealed class JetpackSystem : SharedJetpackSystem
if (_mapManager.TryGetGrid(gridUid, out var grid))
{
coordinates = new EntityCoordinates(grid.Owner, grid.WorldToLocal(coordinates.ToMapPos(EntityManager)));
coordinates = new EntityCoordinates(gridUid.Value, grid.WorldToLocal(coordinates.ToMapPos(EntityManager, _transform)));
}
else if (uidXform.MapUid != null)
{
coordinates = new EntityCoordinates(uidXform.MapUid.Value, uidXform.WorldPosition);
coordinates = new EntityCoordinates(uidXform.MapUid.Value, _transform.GetWorldPosition(uidXform));
}
else
{
return;
}
var ent = Spawn("JetpackEffect", coordinates);
var xform = Transform(ent);
xform.Coordinates = coordinates;
Spawn("JetpackEffect", coordinates);
}
}

View File

@@ -28,6 +28,7 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
[UISystemDependency] private readonly ClientInventorySystem _inventorySystem = default!;
[UISystemDependency] private readonly HandsSystem _handsSystem = default!;
private EntityUid? _playerUid;
private InventorySlotsComponent? _playerInventory;
private readonly Dictionary<string, ItemSlotButtonContainer> _slotGroups = new();
@@ -222,26 +223,26 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
return;
}
if (_playerInventory == null)
if (_playerInventory == null || _playerUid == null)
{
return;
}
if (args.Function == ContentKeyFunctions.ExamineEntity)
{
_inventorySystem.UIInventoryExamine(slot, _playerInventory.Owner);
_inventorySystem.UIInventoryExamine(slot, _playerUid.Value);
}
else if (args.Function == EngineKeyFunctions.UseSecondary)
{
_inventorySystem.UIInventoryOpenContextMenu(slot, _playerInventory.Owner);
_inventorySystem.UIInventoryOpenContextMenu(slot, _playerUid.Value);
}
else if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
{
_inventorySystem.UIInventoryActivateItem(slot, _playerInventory.Owner);
_inventorySystem.UIInventoryActivateItem(slot, _playerUid.Value);
}
else if (args.Function == ContentKeyFunctions.AltActivateItemInWorld)
{
_inventorySystem.UIInventoryAltActivateItem(slot, _playerInventory.Owner);
_inventorySystem.UIInventoryAltActivateItem(slot, _playerUid.Value);
}
}
@@ -258,7 +259,7 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
public void UpdateHover(SlotControl control)
{
var player = _playerInventory?.Owner;
var player = _playerUid;
if (!control.MouseIsHovering ||
_playerInventory == null ||
@@ -305,9 +306,10 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
_inventorySystem.ReloadInventory();
}
private void LoadSlots(InventorySlotsComponent clientInv)
private void LoadSlots(EntityUid clientUid, InventorySlotsComponent clientInv)
{
UnloadSlots();
_playerUid = clientUid;
_playerInventory = clientInv;
foreach (var slotData in clientInv.SlotData.Values)
{
@@ -319,6 +321,7 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
private void UnloadSlots()
{
_playerUid = null;
_playerInventory = null;
foreach (var slotGroup in _slotGroups.Values)
{

View File

@@ -1,7 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.Access
{
@@ -9,6 +12,35 @@ namespace Content.IntegrationTests.Tests.Access
[TestOf(typeof(AccessReaderComponent))]
public sealed class AccessReaderTest
{
[Test]
public async Task TestProtoTags()
{
await using var pair = await PoolManager.GetServerClient(new PoolSettings() { NoClient = true });
var server = pair.Pair.Server;
var protoManager = server.ResolveDependency<IPrototypeManager>();
var accessName = server.ResolveDependency<IComponentFactory>().GetComponentName(typeof(AccessReaderComponent));
await server.WaitAssertion(() =>
{
foreach (var ent in protoManager.EnumeratePrototypes<EntityPrototype>())
{
if (!ent.Components.TryGetComponent(accessName, out var access))
continue;
var reader = (AccessReaderComponent) access;
var allTags = reader.AccessLists.SelectMany(c => c).Union(reader.DenyTags);
foreach (var level in allTags)
{
Assert.That(protoManager.HasIndex<AccessLevelPrototype>(level), $"Invalid access level: {level} found on {ent}");
}
}
});
await pair.CleanReturnAsync();
}
[Test]
public async Task TestTags()
{

View File

@@ -28,10 +28,6 @@ namespace Content.Server.Database.Migrations.Postgres
name: "FK_admin_notes_player_player_user_id",
table: "admin_notes");
migrationBuilder.DropCheckConstraint(
name: "HaveEitherAddressOrUserIdOrHWId",
table: "server_role_ban");
migrationBuilder.DropCheckConstraint(
name: "HaveEitherAddressOrUserIdOrHWId",
table: "server_ban");

View File

@@ -2,5 +2,6 @@ namespace Content.Server.Access.Components
{
[RegisterComponent]
public sealed class AgentIDCardComponent : Component
{}
{
}
}

View File

@@ -4,5 +4,4 @@ namespace Content.Server.Access.Systems;
public sealed class AccessSystem : SharedAccessSystem
{
}

View File

@@ -18,6 +18,7 @@ namespace Content.Server.Access.Systems
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
public override void Initialize()
{
@@ -112,7 +113,7 @@ namespace Content.Server.Access.Systems
if (player != null)
{
_adminLogger.Add(LogType.Identity, LogImpact.Low,
$"{ToPrettyString(player.Value):player} has changed the job title of {ToPrettyString(id.Owner):entity} to {jobTitle} ");
$"{ToPrettyString(player.Value):player} has changed the job title of {ToPrettyString(uid):entity} to {jobTitle} ");
}
return true;
}
@@ -149,7 +150,7 @@ namespace Content.Server.Access.Systems
if (player != null)
{
_adminLogger.Add(LogType.Identity, LogImpact.Low,
$"{ToPrettyString(player.Value):player} has changed the name of {ToPrettyString(id.Owner):entity} to {fullName} ");
$"{ToPrettyString(player.Value):player} has changed the name of {ToPrettyString(uid):entity} to {fullName} ");
}
return true;
}
@@ -174,7 +175,7 @@ namespace Content.Server.Access.Systems
: Loc.GetString("access-id-card-component-owner-full-name-job-title-text",
("fullName", id.FullName),
("jobSuffix", jobSuffix));
EntityManager.GetComponent<MetaDataComponent>(id.Owner).EntityName = val;
_metaSystem.SetEntityName(uid, val);
}
}
}

View File

@@ -21,9 +21,8 @@ public sealed class IdExaminableSystem : EntitySystem
private void OnGetExamineVerbs(EntityUid uid, IdExaminableComponent component, GetVerbsEvent<ExamineVerb> args)
{
var detailsRange = _examineSystem.IsInDetailsRange(args.User, uid);
var info = GetInfo(component.Owner) ?? Loc.GetString("id-examinable-component-verb-no-id");
var info = GetInfo(uid) ?? Loc.GetString("id-examinable-component-verb-no-id");
var verb = new ExamineVerb()
{
@@ -36,7 +35,7 @@ public sealed class IdExaminableSystem : EntitySystem
Category = VerbCategory.Examine,
Disabled = !detailsRange,
Message = Loc.GetString("id-examinable-component-verb-disabled"),
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/character.svg.192dpi.png"))
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/character.svg.192dpi.png"))
};
args.Verbs.Add(verb);
@@ -47,12 +46,13 @@ public sealed class IdExaminableSystem : EntitySystem
if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid))
{
// PDA
if (EntityManager.TryGetComponent(idUid, out PdaComponent? pda) && pda.ContainedId is not null)
if (EntityManager.TryGetComponent(idUid, out PdaComponent? pda) &&
TryComp<IdCardComponent>(pda.ContainedId, out var id))
{
return GetNameAndJob(pda.ContainedId);
return GetNameAndJob(id);
}
// ID Card
if (EntityManager.TryGetComponent(idUid, out IdCardComponent? id))
if (EntityManager.TryGetComponent(idUid, out id))
{
return GetNameAndJob(id);
}

View File

@@ -26,15 +26,16 @@ namespace Content.Server.Access.Systems
{
// Go over all ID cards and make sure they're correctly configured for extended access.
foreach (var card in EntityQuery<PresetIdCardComponent>())
var query = EntityQueryEnumerator<PresetIdCardComponent>();
while (query.MoveNext(out var uid, out var card))
{
var station = _stationSystem.GetOwningStation(card.Owner);
var station = _stationSystem.GetOwningStation(uid);
// If we're not on an extended access station, the ID is already configured correctly from MapInit.
if (station == null || !Comp<StationJobsComponent>(station.Value).ExtendedAccess)
return;
SetupIdAccess(card.Owner, card, true);
SetupIdAccess(uid, card, true);
}
}
@@ -45,7 +46,7 @@ namespace Content.Server.Access.Systems
// or may not yet know whether it is on extended access (players not spawned yet).
// PlayerJobsAssigned makes sure extended access is configured correctly in that case.
var station = _stationSystem.GetOwningStation(id.Owner);
var station = _stationSystem.GetOwningStation(uid);
var extended = false;
if (station != null)
extended = Comp<StationJobsComponent>(station.Value).ExtendedAccess;

View File

@@ -31,6 +31,13 @@ public sealed class BanListEui : BaseEui
_admins.OnPermsChanged += OnPermsChanged;
}
public override void Closed()
{
base.Closed();
_admins.OnPermsChanged -= OnPermsChanged;
}
public override EuiStateBase GetNewState()
{
return new BanListEuiState(BanListPlayerName, Bans);
@@ -42,10 +49,6 @@ public sealed class BanListEui : BaseEui
{
Close();
}
else
{
StateDirty();
}
}
private async Task LoadFromDb()

View File

@@ -99,8 +99,6 @@ public sealed class DepartmentBanCommand : IConsoleCommand
{
_banManager.CreateRoleBan(targetUid, located.Username, shell.Player?.UserId, null, targetHWid, job, minutes, severity, reason, now);
}
_banManager.SendRoleBans(located.UserId);
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Text;
using Content.Server.Administration.Managers;
using Content.Shared.Administration;
@@ -87,7 +87,6 @@ public sealed class RoleBanCommand : IConsoleCommand
var targetHWid = located.LastHWId;
_bans.CreateRoleBan(targetUid, located.Username, shell.Player?.UserId, null, targetHWid, job, minutes, severity, reason, DateTimeOffset.UtcNow);
_bans.SendRoleBans(located.UserId);
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)

View File

@@ -2,6 +2,7 @@ using Content.Server.Administration.UI;
using Content.Server.EUI;
using Content.Server.Hands.Systems;
using Content.Server.Preferences.Managers;
using Content.Shared.Access.Components;
using Content.Shared.Administration;
using Content.Shared.Hands.Components;
using Content.Shared.Inventory;
@@ -16,7 +17,7 @@ using Robust.Shared.Prototypes;
namespace Content.Server.Administration.Commands
{
[AdminCommand(AdminFlags.Admin)]
sealed class SetOutfitCommand : IConsoleCommand
public sealed class SetOutfitCommand : IConsoleCommand
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IPrototypeManager _prototypes = default!;
@@ -25,7 +26,7 @@ namespace Content.Server.Administration.Commands
public string Description => Loc.GetString("set-outfit-command-description", ("requiredComponent", nameof(InventoryComponent)));
public string Help => Loc.GetString("set-outfit-command-help-text", ("command",Command));
public string Help => Loc.GetString("set-outfit-command-help-text", ("command", Command));
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
@@ -51,7 +52,7 @@ namespace Content.Server.Administration.Commands
if (!_entities.HasComponent<InventoryComponent?>(target))
{
shell.WriteLine(Loc.GetString("shell-target-entity-does-not-have-message",("missing", "inventory")));
shell.WriteLine(Loc.GetString("shell-target-entity-does-not-have-message", ("missing", "inventory")));
return;
}
@@ -106,9 +107,9 @@ namespace Content.Server.Administration.Commands
var equipmentEntity = entityManager.SpawnEntity(gearStr, entityManager.GetComponent<TransformComponent>(target).Coordinates);
if (slot.Name == "id" &&
entityManager.TryGetComponent<PdaComponent?>(equipmentEntity, out var pdaComponent) &&
pdaComponent.ContainedId != null)
entityManager.TryGetComponent<IdCardComponent>(pdaComponent.ContainedId, out var id))
{
pdaComponent.ContainedId.FullName = entityManager.GetComponent<MetaDataComponent>(target).EntityName;
id.FullName = entityManager.GetComponent<MetaDataComponent>(target).EntityName;
}
invSystem.TryEquip(target, equipmentEntity, slot.Name, silent: true, force: true, inventory: inventoryComponent);

View File

@@ -40,6 +40,8 @@ public sealed class BanManager : IBanManager, IPostInjectInit
public void Initialize()
{
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
_netManager.RegisterNetMessage<MsgRoleBans>();
}
private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
@@ -212,6 +214,11 @@ public sealed class BanManager : IBanManager, IPostInjectInit
var length = expires == null ? Loc.GetString("cmd-roleban-inf") : Loc.GetString("cmd-roleban-until", ("expires", expires));
_chat.SendAdminAlert(Loc.GetString("cmd-roleban-success", ("target", targetUsername ?? "null"), ("role", role), ("reason", reason), ("length", length)));
if (target != null)
{
SendRoleBans(target.Value);
}
}
public HashSet<string>? GetJobBans(NetUserId playerUserId)

View File

@@ -10,6 +10,7 @@ using Content.Server.Damage.Components;
using Content.Server.Doors.Systems;
using Content.Server.Hands.Systems;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Stack;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
@@ -49,6 +50,9 @@ public sealed partial class AdminVerbSystem
[Dependency] private readonly AdminTestArenaSystem _adminTestArenaSystem = default!;
[Dependency] private readonly StationJobsSystem _stationJobsSystem = default!;
[Dependency] private readonly JointSystem _jointSystem = default!;
[Dependency] private readonly BatterySystem _batterySystem = default!;
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
private void AddTricksVerbs(GetVerbsEvent<Verb> args)
{
@@ -80,7 +84,6 @@ public sealed partial class AdminVerbSystem
? "admin-trick-unbolt-description"
: "admin-trick-bolt-description"),
Priority = (int) (bolts.BoltsDown ? TricksVerbPriorities.Unbolt : TricksVerbPriorities.Bolt),
};
args.Verbs.Add(bolt);
}
@@ -91,7 +94,7 @@ public sealed partial class AdminVerbSystem
{
Text = airlock.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/emergency_access.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/emergency_access.png")),
Act = () =>
{
_airlockSystem.ToggleEmergencyAccess(args.Target, airlock);
@@ -100,9 +103,7 @@ public sealed partial class AdminVerbSystem
Message = Loc.GetString(airlock.EmergencyAccess
? "admin-trick-emergency-access-off-description"
: "admin-trick-emergency-access-on-description"),
Priority = (int) (airlock.EmergencyAccess
? TricksVerbPriorities.EmergencyAccessOff
: TricksVerbPriorities.EmergencyAccessOn),
Priority = (int) (airlock.EmergencyAccess ? TricksVerbPriorities.EmergencyAccessOff : TricksVerbPriorities.EmergencyAccessOn),
};
args.Verbs.Add(emergencyAccess);
}
@@ -113,7 +114,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Rejuvenate",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/rejuvenate.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rejuvenate.png")),
Act = () =>
{
RejuvenateCommand.PerformRejuvenate(args.Target);
@@ -131,7 +132,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Make Indestructible",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
Act = () =>
{
_godmodeSystem.EnableGodmode(args.Target);
@@ -148,7 +149,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Make Vulnerable",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
Act = () =>
{
_godmodeSystem.DisableGodmode(args.Target);
@@ -166,11 +167,11 @@ public sealed partial class AdminVerbSystem
{
Text = "Refill Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/fill_battery.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
Act = () =>
{
battery.CurrentCharge = battery.MaxCharge;
Dirty(battery);
_batterySystem.SetCharge(args.Target, battery.MaxCharge, battery);
Dirty(args.Target, battery);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-refill-battery-description"),
@@ -182,11 +183,11 @@ public sealed partial class AdminVerbSystem
{
Text = "Drain Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/drain_battery.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
Act = () =>
{
battery.CurrentCharge = 0;
Dirty(battery);
_batterySystem.SetCharge(args.Target, 0, battery);
Dirty(args.Target, battery);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-drain-battery-description"),
@@ -198,7 +199,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Infinite Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/infinite_battery.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/infinite_battery.png")),
Act = () =>
{
var recharger = EnsureComp<BatterySelfRechargerComponent>(args.Target);
@@ -218,7 +219,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Block Unanchoring",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/anchor.svg.192dpi.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/anchor.svg.192dpi.png")),
Act = () =>
{
RemComp(args.Target, anchor);
@@ -236,7 +237,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Refill Internals Oxygen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
Act = () =>
{
RefillGasTank(args.Target, Gas.Oxygen, tank);
@@ -251,7 +252,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Refill Internals Nitrogen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Tanks/red.rsi"), "icon"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
Act = () =>
{
RefillGasTank(args.Target, Gas.Nitrogen, tank);
@@ -266,7 +267,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Refill Internals Plasma",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
Act = () =>
{
RefillGasTank(args.Target, Gas.Plasma, tank);
@@ -284,7 +285,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Refill Internals Oxygen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
Act = () =>
{
foreach (var slot in _inventorySystem.GetSlots(args.Target))
@@ -316,7 +317,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Refill Internals Nitrogen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Tanks/red.rsi"), "icon"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
Act = () =>
{
foreach (var slot in _inventorySystem.GetSlots(args.Target))
@@ -348,7 +349,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Refill Internals Plasma",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
Act = () =>
{
foreach (var slot in _inventorySystem.GetSlots(args.Target))
@@ -381,12 +382,12 @@ public sealed partial class AdminVerbSystem
{
Text = "Send to test arena",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")),
Act = () =>
{
var (mapUid, gridUid) = _adminTestArenaSystem.AssertArenaLoaded(player);
Transform(args.Target).Coordinates = new EntityCoordinates(gridUid ?? mapUid, Vector2.One);
_xformSystem.SetCoordinates(args.Target, new EntityCoordinates(gridUid ?? mapUid, Vector2.One));
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-send-to-test-arena-description"),
@@ -402,7 +403,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Grant All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
Act = () =>
{
GiveAllAccess(activeId.Value);
@@ -417,7 +418,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Revoke All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Misc/id_cards.rsi"), "default"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "default"),
Act = () =>
{
RevokeAllAccess(activeId.Value);
@@ -435,7 +436,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Grant All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
Act = () =>
{
GiveAllAccess(args.Target);
@@ -450,7 +451,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Revoke All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Misc/id_cards.rsi"), "default"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "default"),
Act = () =>
{
RevokeAllAccess(args.Target);
@@ -469,7 +470,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Adjust Stack",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/adjust-stack.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/adjust-stack.png")),
Act = () =>
{
// Unbounded intentionally.
@@ -488,7 +489,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Fill Stack",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/fill-stack.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill-stack.png")),
Act = () =>
{
_stackSystem.SetCount(args.Target, _stackSystem.GetMaxCount(stack), stack);
@@ -504,12 +505,12 @@ public sealed partial class AdminVerbSystem
{
Text = "Rename",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/rename.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rename.png")),
Act = () =>
{
_quickDialog.OpenDialog(player, "Rename", "Name", (string newName) =>
{
MetaData(args.Target).EntityName = newName;
_metaSystem.SetEntityName(args.Target, newName);
});
},
Impact = LogImpact.Medium,
@@ -522,12 +523,12 @@ public sealed partial class AdminVerbSystem
{
Text = "Redescribe",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/redescribe.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/redescribe.png")),
Act = () =>
{
_quickDialog.OpenDialog(player, "Redescribe", "Description", (LongString newDescription) =>
{
MetaData(args.Target).EntityDescription = newDescription.String;
_metaSystem.SetEntityDescription(args.Target, newDescription.String);
});
},
Impact = LogImpact.Medium,
@@ -540,15 +541,15 @@ public sealed partial class AdminVerbSystem
{
Text = "Redescribe",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/rename_and_redescribe.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rename_and_redescribe.png")),
Act = () =>
{
_quickDialog.OpenDialog(player, "Rename & Redescribe", "Name", "Description",
(string newName, LongString newDescription) =>
{
var meta = MetaData(args.Target);
meta.EntityName = newName;
meta.EntityDescription = newDescription.String;
_metaSystem.SetEntityName(args.Target, newName, meta);
_metaSystem.SetEntityDescription(args.Target, newDescription.String, meta);
});
},
Impact = LogImpact.Medium,
@@ -565,7 +566,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Bar job slots",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/bar_jobslots.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/bar_jobslots.png")),
Act = () =>
{
foreach (var (job, _) in _stationJobsSystem.GetJobs(args.Target))
@@ -584,7 +585,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Locate Cargo Shuttle",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Head/Soft/cargosoft.rsi"), "icon"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Clothing/Head/Soft/cargosoft.rsi"), "icon"),
Act = () =>
{
var shuttle = Comp<StationCargoOrderDatabaseComponent>(args.Target).Shuttle;
@@ -592,7 +593,7 @@ public sealed partial class AdminVerbSystem
if (shuttle is null)
return;
Transform(args.User).Coordinates = new EntityCoordinates(shuttle.Value, Vector2.Zero);
_xformSystem.SetCoordinates(args.User, new EntityCoordinates(shuttle.Value, Vector2.Zero));
},
Impact = LogImpact.Low,
Message = Loc.GetString("admin-trick-locate-cargo-shuttle-description"),
@@ -607,7 +608,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Refill Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/fill_battery.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
Act = () =>
{
foreach (var ent in childEnum)
@@ -615,8 +616,8 @@ public sealed partial class AdminVerbSystem
if (!HasComp<StationInfiniteBatteryTargetComponent>(ent))
continue;
var battery = EnsureComp<BatteryComponent>(ent);
battery.CurrentCharge = battery.MaxCharge;
Dirty(battery);
_batterySystem.SetCharge(ent, battery.MaxCharge, battery);
Dirty(ent, battery);
}
},
Impact = LogImpact.Extreme,
@@ -629,7 +630,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Drain Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/drain_battery.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
Act = () =>
{
foreach (var ent in childEnum)
@@ -637,8 +638,8 @@ public sealed partial class AdminVerbSystem
if (!HasComp<StationInfiniteBatteryTargetComponent>(ent))
continue;
var battery = EnsureComp<BatteryComponent>(ent);
battery.CurrentCharge = 0;
Dirty(battery);
_batterySystem.SetCharge(ent, 0, battery);
Dirty(ent, battery);
}
},
Impact = LogImpact.Extreme,
@@ -651,7 +652,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Infinite Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/infinite_battery.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/infinite_battery.png")),
Act = () =>
{
// this kills the sloth
@@ -680,7 +681,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Halt Movement",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/halt.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/halt.png")),
Act = () =>
{
_physics.SetLinearVelocity(args.Target, Vector2.Zero, body: physics);
@@ -703,7 +704,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Unpause Map",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/play.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/play.png")),
Act = () =>
{
_mapManager.SetMapPaused(map.MapId, false);
@@ -720,7 +721,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Pause Map",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/pause.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/pause.png")),
Act = () =>
{
_mapManager.SetMapPaused(map.MapId, true);
@@ -740,10 +741,10 @@ public sealed partial class AdminVerbSystem
{
Text = "Snap Joints",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/snap_joints.png")),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/snap_joints.png")),
Act = () =>
{
_jointSystem.ClearJoints(joints);
_jointSystem.ClearJoints(args.Target, joints);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-snap-joints-description"),
@@ -758,7 +759,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Make Minigun",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Weapons/Guns/HMGs/minigun.rsi"), "icon"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Weapons/Guns/HMGs/minigun.rsi"), "icon"),
Act = () =>
{
gun.FireRate = 15;
@@ -776,7 +777,7 @@ public sealed partial class AdminVerbSystem
{
Text = "Set Bullet Amount",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/caps.rsi"), "mag-6"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Fun/caps.rsi"), "mag-6"),
Act = () =>
{
_quickDialog.OpenDialog(player, "Set Bullet Amount", $"Amount (max {ballisticAmmo.Capacity}):", (int amount) =>
@@ -858,12 +859,10 @@ public sealed partial class AdminVerbSystem
{
return slotEntity.Value;
}
else if (TryComp<PdaComponent>(slotEntity, out var pda))
else if (TryComp<PdaComponent>(slotEntity, out var pda)
&& HasComp<IdCardComponent>(pda.ContainedId))
{
if (pda.ContainedId != null)
{
return pda.ContainedId.Owner;
}
return pda.ContainedId;
}
}
else if (TryComp<HandsComponent>(target, out var hands))
@@ -891,40 +890,40 @@ public sealed partial class AdminVerbSystem
private void RevokeAllAccess(EntityUid entity)
{
_accessSystem.TrySetTags(entity, new string[]{});
_accessSystem.TrySetTags(entity, Array.Empty<string>());
}
public enum TricksVerbPriorities
{
Bolt = 0,
Unbolt = 0,
EmergencyAccessOn = -1, // These are separate intentionally for `invokeverb` shenanigans.
EmergencyAccessOff = -1,
MakeIndestructible = -2,
MakeVulnerable = -2,
BlockUnanchoring = -3,
RefillBattery = -4,
DrainBattery = -5,
RefillOxygen = -6,
RefillNitrogen = -7,
RefillPlasma = -8,
SendToTestArena = -9,
GrantAllAccess = -10,
RevokeAllAccess = -11,
Rejuvenate = -12,
AdjustStack = -13,
FillStack = -14,
Rename = -15,
Redescribe = -16,
RenameAndRedescribe = -17,
BarJobSlots = -18,
LocateCargoShuttle = -19,
InfiniteBattery = -20,
HaltMovement = -21,
Unpause = -22,
Pause = -23,
SnapJoints = -24,
MakeMinigun = -25,
SetBulletAmount = -26,
Unbolt = -1,
EmergencyAccessOn = -2,
EmergencyAccessOff = -3,
MakeIndestructible = -4,
MakeVulnerable = -5,
BlockUnanchoring = -6,
RefillBattery = -7,
DrainBattery = -8,
RefillOxygen = -9,
RefillNitrogen = -10,
RefillPlasma = -11,
SendToTestArena = -12,
GrantAllAccess = -13,
RevokeAllAccess = -14,
Rejuvenate = -15,
AdjustStack = -16,
FillStack = -17,
Rename = -18,
Redescribe = -19,
RenameAndRedescribe = -20,
BarJobSlots = -21,
LocateCargoShuttle = -22,
InfiniteBattery = -23,
HaltMovement = -24,
Unpause = -25,
Pause = -26,
SnapJoints = -27,
MakeMinigun = -28,
SetBulletAmount = -29,
}
}

View File

@@ -15,7 +15,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="All" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Content.Packaging\Content.Packaging.csproj" />

View File

@@ -1312,70 +1312,67 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
// You can't group queries, as player will not always exist. When it doesn't, the
// whole query returns nothing
var player = await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == user);
return await (from ban in db.DbContext.Ban
where ban.PlayerUserId == user &&
!ban.Hidden
select ban)
var bans = await db.DbContext.Ban
.Where(ban => ban.PlayerUserId == user && !ban.Hidden)
.Include(ban => ban.Unban)
.Include(ban => ban.Round)
.ThenInclude(r => r!.Server)
.Include(ban => ban.CreatedBy)
.Include(ban => ban.LastEditedBy)
.Include(ban => ban.Unban)
.ToAsyncEnumerable()
.SelectAwait(async ban =>
new ServerBanNote(ban.Id, ban.RoundId, ban.Round, ban.PlayerUserId, player,
ban.PlaytimeAtNote, ban.Reason, ban.Severity, ban.CreatedBy, ban.BanTime,
ban.LastEditedBy, ban.LastEditedAt, ban.ExpirationTime, ban.Hidden,
ban.Unban?.UnbanningAdmin == null
? null
: await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == ban.Unban.UnbanningAdmin.Value),
ban.Unban?.UnbanTime)
).ToListAsync();
.ToArrayAsync();
var banNotes = new List<ServerBanNote>();
foreach (var ban in bans)
{
var banNote = new ServerBanNote(ban.Id, ban.RoundId, ban.Round, ban.PlayerUserId, player,
ban.PlaytimeAtNote, ban.Reason, ban.Severity, ban.CreatedBy, ban.BanTime,
ban.LastEditedBy, ban.LastEditedAt, ban.ExpirationTime, ban.Hidden,
ban.Unban?.UnbanningAdmin == null
? null
: await db.DbContext.Player.SingleOrDefaultAsync(
p => p.UserId == ban.Unban.UnbanningAdmin.Value),
ban.Unban?.UnbanTime);
banNotes.Add(banNote);
}
return banNotes;
}
protected async Task<List<ServerRoleBanNote>> GetGroupedServerRoleBansAsNotesForUser(DbGuard db, Guid user)
{
// Server side query
var bansQuery =
(from ban in db.DbContext.RoleBan
where ban.PlayerUserId == user &&
!ban.Hidden
select ban)
var bansQuery = await db.DbContext.RoleBan
.Where(ban => ban.PlayerUserId == user && !ban.Hidden)
.Include(ban => ban.Unban)
.Include(ban => ban.Round)
.ThenInclude(r => r!.Server)
.Include(ban => ban.CreatedBy)
.Include(ban => ban.LastEditedBy)
.Include(ban => ban.Unban)
.ToAsyncEnumerable();
.ToArrayAsync();
// Client side query, as EF can't do groups yet
var bansEnumerable =
(from ban in bansQuery
group ban by new
{
ban.BanTime,
ban.CreatedBy,
ban.Reason,
Unbanned = ban.Unban == null
}
into banGroup
select banGroup)
.AsAsyncEnumerable();
var bansEnumerable = bansQuery
.GroupBy(ban => new { ban.BanTime, CreatedBy = (Player?)ban.CreatedBy, ban.Reason, Unbanned = ban.Unban == null })
.Select(banGroup => banGroup)
.ToArray();
List<ServerRoleBanNote> bans = new();
var player = await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == user);
await foreach (var banGroup in bansEnumerable)
foreach (var banGroup in bansEnumerable)
{
var firstBan = await banGroup.FirstAsync();
var firstBan = banGroup.First();
Player? unbanningAdmin = null;
if (firstBan.Unban?.UnbanningAdmin is not null)
unbanningAdmin = await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == firstBan.Unban.UnbanningAdmin.Value);
bans.Add(new ServerRoleBanNote(firstBan.Id, firstBan.RoundId, firstBan.Round, firstBan.PlayerUserId,
player, firstBan.PlaytimeAtNote, firstBan.Reason, firstBan.Severity, firstBan.CreatedBy,
firstBan.BanTime, firstBan.LastEditedBy, firstBan.LastEditedAt, firstBan.ExpirationTime,
firstBan.Hidden, await banGroup.Select(ban => ban.RoleId.Replace(BanManager.JobPrefix, null)).ToArrayAsync(),
firstBan.Hidden, banGroup.Select(ban => ban.RoleId.Replace(BanManager.JobPrefix, null)).ToArray(),
unbanningAdmin, firstBan.Unban?.UnbanTime));
}

View File

@@ -1,18 +1,10 @@
using System.Numerics;
namespace Content.Server.Movement.Components
namespace Content.Server.Movement.Components;
[RegisterComponent]
public sealed class StressTestMovementComponent : Component
{
[RegisterComponent]
public sealed class StressTestMovementComponent : Component
{
public float Progress { get; set; }
public Vector2 Origin { get; set; }
protected override void Startup()
{
base.Startup();
Origin = IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(Owner).WorldPosition;
}
}
public float Progress { get; set; }
public Vector2 Origin { get; set; }
}

View File

@@ -1,32 +1,42 @@
using System.Numerics;
using Content.Server.Movement.Components;
using JetBrains.Annotations;
namespace Content.Server.Movement
namespace Content.Server.Movement;
public sealed class StressTestMovementSystem : EntitySystem
{
[UsedImplicitly]
internal sealed class StressTestMovementSystem : EntitySystem
[Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize()
{
public override void Update(float frameTime)
base.Initialize();
SubscribeLocalEvent<StressTestMovementComponent, ComponentStartup>(OnStressStartup);
}
private void OnStressStartup(EntityUid uid, StressTestMovementComponent component, ComponentStartup args)
{
component.Origin = _transform.GetWorldPosition(uid);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<StressTestMovementComponent, TransformComponent>();
while (query.MoveNext(out var uid, out var stressTest, out var transform))
{
base.Update(frameTime);
stressTest.Progress += frameTime;
foreach (var stressTest in EntityManager.EntityQuery<StressTestMovementComponent>(true))
if (stressTest.Progress > 1)
{
var transform = EntityManager.GetComponent<TransformComponent>(stressTest.Owner);
stressTest.Progress += frameTime;
if (stressTest.Progress > 1)
{
stressTest.Progress -= 1;
}
var x = MathF.Sin(stressTest.Progress * MathHelper.TwoPi);
var y = MathF.Cos(stressTest.Progress * MathHelper.TwoPi);
transform.WorldPosition = stressTest.Origin + (new Vector2(x, y) * 5);
stressTest.Progress -= 1;
}
var x = MathF.Sin(stressTest.Progress * MathHelper.TwoPi);
var y = MathF.Cos(stressTest.Progress * MathHelper.TwoPi);
_transform.SetWorldPosition(transform, stressTest.Origin + new Vector2(x, y) * 5);
}
}
}

View File

@@ -12,38 +12,40 @@ public sealed class JetpackSystem : SharedJetpackSystem
[Dependency] private readonly GasTankSystem _gasTank = default!;
[Dependency] private readonly IGameTiming _timing = default!;
private const float UpdateCooldown = 0.5f;
protected override bool CanEnable(JetpackComponent component)
protected override bool CanEnable(EntityUid uid, JetpackComponent component)
{
return base.CanEnable(component) && TryComp<GasTankComponent>(component.Owner, out var gasTank) && !(gasTank.Air.TotalMoles < component.MoleUsage);
return base.CanEnable(uid, component) &&
TryComp<GasTankComponent>(uid, out var gasTank) &&
!(gasTank.Air.TotalMoles < component.MoleUsage);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var toDisable = new ValueList<JetpackComponent>();
var toDisable = new ValueList<(EntityUid Uid, JetpackComponent Component)>();
var query = EntityQueryEnumerator<ActiveJetpackComponent, JetpackComponent, GasTankComponent>();
foreach (var (active, comp, gasTank) in EntityQuery<ActiveJetpackComponent, JetpackComponent, GasTankComponent>())
while (query.MoveNext(out var uid, out var active, out var comp, out var gasTank))
{
if (_timing.CurTime < active.TargetTime) continue;
if (_timing.CurTime < active.TargetTime)
continue;
active.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(active.EffectCooldown);
var air = _gasTank.RemoveAir(gasTank, comp.MoleUsage);
if (air == null || !MathHelper.CloseTo(air.TotalMoles, comp.MoleUsage, 0.001f))
{
toDisable.Add(comp);
toDisable.Add((uid, comp));
continue;
}
_gasTank.UpdateUserInterface(gasTank);
}
foreach (var comp in toDisable)
foreach (var (uid, comp) in toDisable)
{
SetEnabled(comp, false);
SetEnabled(uid, comp, false);
}
}
}

View File

@@ -9,6 +9,7 @@ using Content.Server.PDA.Ringer;
using Content.Server.Station.Systems;
using Content.Server.Store.Components;
using Content.Server.Store.Systems;
using Content.Shared.Access.Components;
using Content.Shared.PDA;
using Robust.Server.GameObjects;
using Robust.Server.Player;
@@ -117,14 +118,15 @@ namespace Content.Server.PDA
// TODO: Update the level and name of the station with each call to UpdatePdaUi is only needed for latejoin players.
// TODO: If someone can implement changing the level and name of the station when changing the PDA grid, this can be removed.
var id = CompOrNull<IdCardComponent>(pda.ContainedId);
var state = new PdaUpdateState(
pda.FlashlightOn,
pda.PenSlot.HasItem,
new PdaIdInfoText
{
ActualOwnerName = pda.OwnerName,
IdOwner = pda.ContainedId?.FullName,
JobTitle = pda.ContainedId?.JobTitle,
IdOwner = id?.FullName,
JobTitle = id?.JobTitle,
StationAlertLevel = pda.StationAlertLevel,
StationAlertColor = pda.StationAlertColor
},

View File

@@ -93,7 +93,7 @@ namespace Content.Server.Sandbox
if (e.NewStatus != SessionStatus.Connected || e.OldStatus != SessionStatus.Connecting)
return;
RaiseNetworkEvent(new MsgSandboxStatus {SandboxAllowed = IsSandboxEnabled}, e.Session.ConnectedClient);
RaiseNetworkEvent(new MsgSandboxStatus { SandboxAllowed = IsSandboxEnabled }, e.Session.ConnectedClient);
}
private void SandboxRespawnReceived(MsgSandboxRespawn message, EntitySessionEventArgs args)
@@ -113,7 +113,7 @@ namespace Content.Server.Sandbox
return;
var player = _playerManager.GetSessionByChannel(args.SenderSession.ConnectedClient);
if (player.AttachedEntity is not {} attached)
if (player.AttachedEntity is not { } attached)
{
return;
}
@@ -130,17 +130,17 @@ namespace Content.Server.Sandbox
}
else if (TryComp<PdaComponent>(slotEntity, out var pda))
{
if (pda.ContainedId == null)
if (pda.ContainedId is null)
{
var newID = CreateFreshId();
if (TryComp<ItemSlotsComponent>(pda.Owner, out var itemSlots))
if (TryComp<ItemSlotsComponent>(slotEntity, out var itemSlots))
{
_slots.TryInsert(slotEntity.Value, pda.IdSlot, newID, null);
}
}
else
{
UpgradeId(pda.ContainedId.Owner);
UpgradeId(pda.ContainedId!.Value);
}
}
}
@@ -189,7 +189,7 @@ namespace Content.Server.Sandbox
private void UpdateSandboxStatusForAll()
{
RaiseNetworkEvent(new MsgSandboxStatus {SandboxAllowed = IsSandboxEnabled});
RaiseNetworkEvent(new MsgSandboxStatus { SandboxAllowed = IsSandboxEnabled });
}
}
}

View File

@@ -7,6 +7,7 @@ using Content.Server.Mind.Commands;
using Content.Server.PDA;
using Content.Server.Roles;
using Content.Server.Station.Components;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.CCVar;
using Content.Shared.Hands.Components;
@@ -44,6 +45,7 @@ public sealed class StationSpawningSystem : EntitySystem
[Dependency] private readonly PdaSystem _pdaSystem = default!;
[Dependency] private readonly SharedAccessSystem _accessSystem = default!;
[Dependency] private readonly IdentitySystem _identity = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
private bool _randomizeCharacters;
@@ -73,7 +75,7 @@ public sealed class StationSpawningSystem : EntitySystem
var ev = new PlayerSpawningEvent(job, profile, station);
RaiseLocalEvent(ev);
DebugTools.Assert(ev.SpawnResult is {Valid: true} or null);
DebugTools.Assert(ev.SpawnResult is { Valid: true } or null);
return ev.SpawnResult;
}
@@ -146,7 +148,7 @@ public sealed class StationSpawningSystem : EntitySystem
if (profile != null)
{
_humanoidSystem.LoadProfile(entity.Value, profile);
MetaData(entity.Value).EntityName = profile.Name;
_metaSystem.SetEntityName(entity.Value, profile.Name);
if (profile.FlavorText != "" && _configurationManager.GetCVar(CCVars.FlavorText))
{
AddComp<DetailExaminableComponent>(entity.Value).Content = profile.FlavorText;
@@ -158,7 +160,7 @@ public sealed class StationSpawningSystem : EntitySystem
return entity.Value;
}
private void DoJobSpecials(Job? job, EntityUid entity)
private static void DoJobSpecials(Job? job, EntityUid entity)
{
foreach (var jobSpecial in job?.Prototype.Special ?? Array.Empty<JobSpecial>())
{
@@ -211,11 +213,10 @@ public sealed class StationSpawningSystem : EntitySystem
if (!_inventorySystem.TryGetSlotEntity(entity, "id", out var idUid))
return;
if (!EntityManager.TryGetComponent(idUid, out PdaComponent? pdaComponent) || pdaComponent.ContainedId == null)
if (!EntityManager.TryGetComponent(idUid, out PdaComponent? pdaComponent) || !TryComp<IdCardComponent>(pdaComponent.ContainedId, out var card))
return;
var card = pdaComponent.ContainedId;
var cardId = card.Owner;
var cardId = pdaComponent.ContainedId.Value;
_cardSystem.TryChangeFullName(cardId, characterName, card);
_cardSystem.TryChangeJobTitle(cardId, jobPrototype.LocalizedName, card);

View File

@@ -1,9 +1,7 @@
using System.Linq;
using Content.Server.Anomaly;
using Content.Server.Anomaly;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Station.Components;
using Content.Server.StationEvents.Components;
using Robust.Shared.Random;
namespace Content.Server.StationEvents.Events;
@@ -30,13 +28,9 @@ public sealed class AnomalySpawnRule : StationEventSystem<AnomalySpawnRuleCompon
if (!TryComp<StationDataComponent>(chosenStation, out var stationData))
return;
EntityUid? grid = null;
foreach (var g in stationData.Grids.Where(HasComp<BecomesStationComponent>))
{
grid = g;
}
var grid = StationSystem.GetLargestGrid(stationData);
if (grid is not { })
if (grid is null)
return;
var amountToSpawn = Math.Max(1, (int) MathF.Round(GetSeverityModifier() / 2));

View File

@@ -59,7 +59,7 @@ public sealed class StationRecordsSystem : EntitySystem
string? jobId, StationRecordsComponent? records = null)
{
if (!Resolve(station, ref records)
|| String.IsNullOrEmpty(jobId)
|| string.IsNullOrEmpty(jobId)
|| !_prototypeManager.HasIndex<JobPrototype>(jobId))
{
return;
@@ -204,7 +204,7 @@ public sealed class StationRecordsSystem : EntitySystem
{
if (!Resolve(station, ref records))
{
return new (StationRecordKey, T)[]{};
return Array.Empty<(StationRecordKey, T)>();
}
return records.Records.GetRecordsOfType<T>();

View File

@@ -50,12 +50,13 @@ namespace Content.Server.Tabletop
TabletopMap = _mapManager.CreateMap();
_tabletops = 0;
var mapUid = _mapManager.GetMapEntityId(TabletopMap);
var mapComp = EntityManager.GetComponent<MapComponent>(_mapManager.GetMapEntityId(TabletopMap));
var mapComp = EntityManager.GetComponent<MapComponent>(mapUid);
// Lighting is always disabled in tabletop world.
mapComp.LightingEnabled = false;
mapComp.Dirty();
Dirty(mapUid, mapComp);
}
/// <summary>

View File

@@ -22,7 +22,7 @@ namespace Content.Server.Verbs.Commands
}
var entityManager = IoCManager.Resolve<IEntityManager>();
var verbSystem = EntitySystem.Get<SharedVerbSystem>();
var verbSystem = entityManager.System<SharedVerbSystem>();
// get the 'player' entity (defaulting to command user, otherwise uses a uid)
EntityUid? playerEntity = null;

View File

@@ -14,6 +14,6 @@ public sealed class AccessGroupPrototype : IPrototype
[IdDataField]
public string ID { get; } = default!;
[DataField("tags", required: true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<AccessLevelPrototype>))]
[DataField("tags", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessLevelPrototype>))]
public HashSet<string> Tags = default!;
}

View File

@@ -1,4 +1,3 @@
using Content.Shared.Access.Systems;
using Content.Shared.StationRecords;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
@@ -59,4 +58,3 @@ public sealed class AccessReaderComponentState : ComponentState
AccessKeys = accessKeys;
}
}

View File

@@ -1,7 +1,6 @@
using Content.Shared.Access.Systems;
using Content.Shared.PDA;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Access.Components
{
@@ -17,8 +16,7 @@ namespace Content.Shared.Access.Components
[DataField("jobTitle")]
[AutoNetworkedField]
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem),
Other = AccessPermissions.ReadWrite)]
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWrite)]
public string? JobTitle;
}
}

View File

@@ -48,6 +48,7 @@ namespace Content.Shared.Access.Systems
public sealed class AgentIDCardJobChangedMessage : BoundUserInterfaceMessage
{
public string Job { get; }
public AgentIDCardJobChangedMessage(string job)
{
Job = job;

View File

@@ -11,286 +11,272 @@ using Content.Shared.Hands.EntitySystems;
using Content.Shared.StationRecords;
using Robust.Shared.GameStates;
namespace Content.Shared.Access.Systems
namespace Content.Shared.Access.Systems;
public sealed class AccessReaderSystem : EntitySystem
{
public sealed class AccessReaderSystem : EntitySystem
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
public override void Initialize()
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
base.Initialize();
public override void Initialize()
SubscribeLocalEvent<AccessReaderComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<AccessReaderComponent, LinkAttemptEvent>(OnLinkAttempt);
SubscribeLocalEvent<AccessReaderComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<AccessReaderComponent, ComponentHandleState>(OnHandleState);
}
private void OnGetState(EntityUid uid, AccessReaderComponent component, ref ComponentGetState args)
{
args.State = new AccessReaderComponentState(component.Enabled, component.DenyTags, component.AccessLists,
component.AccessKeys);
}
private void OnHandleState(EntityUid uid, AccessReaderComponent component, ref ComponentHandleState args)
{
if (args.Current is not AccessReaderComponentState state)
return;
component.Enabled = state.Enabled;
component.AccessKeys = new (state.AccessKeys);
component.AccessLists = new (state.AccessLists);
component.DenyTags = new (state.DenyTags);
}
private void OnLinkAttempt(EntityUid uid, AccessReaderComponent component, LinkAttemptEvent args)
{
if (args.User == null) // AutoLink (and presumably future external linkers) have no user.
return;
if (!HasComp<EmaggedComponent>(uid) && !IsAllowed(args.User.Value, component))
args.Cancel();
}
private void OnEmagged(EntityUid uid, AccessReaderComponent reader, ref GotEmaggedEvent args)
{
args.Handled = true;
reader.Enabled = false;
Dirty(reader);
}
/// <summary>
/// Searches the source for access tags
/// then compares it with the targets readers access list to see if it is allowed.
/// </summary>
/// <param name="source">The entity that wants access.</param>
/// <param name="target">The entity to search for an access reader</param>
/// <param name="reader">Optional reader from the target entity</param>
public bool IsAllowed(EntityUid source, EntityUid target, AccessReaderComponent? reader = null)
{
if (!Resolve(target, ref reader, false))
return true;
return IsAllowed(source, reader);
}
/// <summary>
/// Searches the given entity for access tags
/// then compares it with the readers access list to see if it is allowed.
/// </summary>
/// <param name="entity">The entity that wants access.</param>
/// <param name="reader">A reader from a different entity</param>
public bool IsAllowed(EntityUid entity, AccessReaderComponent reader)
{
var allEnts = FindPotentialAccessItems(entity);
// Access reader is totally disabled, so access is always allowed.
if (!reader.Enabled)
return true;
if (AreAccessTagsAllowed(FindAccessTags(entity, allEnts), reader))
return true;
if (AreStationRecordKeysAllowed(FindStationRecordKeys(entity, allEnts), reader))
return true;
return false;
}
/// <summary>
/// Compares the given tags with the readers access list to see if it is allowed.
/// </summary>
/// <param name="accessTags">A list of access tags</param>
/// <param name="reader">An access reader to check against</param>
public bool AreAccessTagsAllowed(ICollection<string> accessTags, AccessReaderComponent reader)
{
if (reader.DenyTags.Overlaps(accessTags))
{
base.Initialize();
SubscribeLocalEvent<AccessReaderComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<AccessReaderComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<AccessReaderComponent, LinkAttemptEvent>(OnLinkAttempt);
SubscribeLocalEvent<AccessReaderComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<AccessReaderComponent, ComponentHandleState>(OnHandleState);
}
private void OnGetState(EntityUid uid, AccessReaderComponent component, ref ComponentGetState args)
{
args.State = new AccessReaderComponentState(component.Enabled, component.DenyTags, component.AccessLists,
component.AccessKeys);
}
private void OnHandleState(EntityUid uid, AccessReaderComponent component, ref ComponentHandleState args)
{
if (args.Current is not AccessReaderComponentState state)
return;
component.Enabled = state.Enabled;
component.AccessKeys = new (state.AccessKeys);
component.AccessLists = new (state.AccessLists);
component.DenyTags = new (state.DenyTags);
}
private void OnLinkAttempt(EntityUid uid, AccessReaderComponent component, LinkAttemptEvent args)
{
if (args.User == null) // AutoLink (and presumably future external linkers) have no user.
return;
if (!HasComp<EmaggedComponent>(uid) && !IsAllowed(args.User.Value, component))
args.Cancel();
}
private void OnInit(EntityUid uid, AccessReaderComponent reader, ComponentInit args)
{
var allTags = reader.AccessLists.SelectMany(c => c).Union(reader.DenyTags);
foreach (var level in allTags)
{
if (!_prototypeManager.HasIndex<AccessLevelPrototype>(level))
{
Logger.ErrorS("access", $"Invalid access level: {level}");
}
}
}
private void OnEmagged(EntityUid uid, AccessReaderComponent reader, ref GotEmaggedEvent args)
{
args.Handled = true;
reader.Enabled = false;
Dirty(reader);
}
/// <summary>
/// Searches the source for access tags
/// then compares it with the targets readers access list to see if it is allowed.
/// </summary>
/// <param name="source">The entity that wants access.</param>
/// <param name="target">The entity to search for an access reader</param>
/// <param name="reader">Optional reader from the target entity</param>
public bool IsAllowed(EntityUid source, EntityUid target, AccessReaderComponent? reader = null)
{
if (!Resolve(target, ref reader, false))
return true;
return IsAllowed(source, reader);
}
/// <summary>
/// Searches the given entity for access tags
/// then compares it with the readers access list to see if it is allowed.
/// </summary>
/// <param name="entity">The entity that wants access.</param>
/// <param name="reader">A reader from a different entity</param>
public bool IsAllowed(EntityUid entity, AccessReaderComponent reader)
{
var allEnts = FindPotentialAccessItems(entity);
// Access reader is totally disabled, so access is always allowed.
if (!reader.Enabled)
return true;
if (AreAccessTagsAllowed(FindAccessTags(entity, allEnts), reader))
return true;
if (AreStationRecordKeysAllowed(FindStationRecordKeys(entity, allEnts), reader))
return true;
// Sec owned by cargo.
// Note that in resolving the issue with only one specific item "counting" for access, this became a bit more strict.
// As having an ID card in any slot that "counts" with a denied access group will cause denial of access.
// DenyTags doesn't seem to be used right now anyway, though, so it'll be dependent on whoever uses it to figure out if this matters.
return false;
}
/// <summary>
/// Compares the given tags with the readers access list to see if it is allowed.
/// </summary>
/// <param name="accessTags">A list of access tags</param>
/// <param name="reader">An access reader to check against</param>
public bool AreAccessTagsAllowed(ICollection<string> accessTags, AccessReaderComponent reader)
return reader.AccessLists.Count == 0 || reader.AccessLists.Any(a => a.IsSubsetOf(accessTags));
}
/// <summary>
/// Compares the given stationrecordkeys with the accessreader to see if it is allowed.
/// </summary>
public bool AreStationRecordKeysAllowed(ICollection<StationRecordKey> keys, AccessReaderComponent reader)
{
return keys.Any() && reader.AccessKeys.Any(keys.Contains);
}
/// <summary>
/// Finds all the items that could potentially give access to a given entity
/// </summary>
public HashSet<EntityUid> FindPotentialAccessItems(EntityUid uid)
{
FindAccessItemsInventory(uid, out var items);
var ev = new GetAdditionalAccessEvent
{
if (reader.DenyTags.Overlaps(accessTags))
{
// Sec owned by cargo.
Entities = items
};
RaiseLocalEvent(uid, ref ev);
items.Add(uid);
return items;
}
// Note that in resolving the issue with only one specific item "counting" for access, this became a bit more strict.
// As having an ID card in any slot that "counts" with a denied access group will cause denial of access.
// DenyTags doesn't seem to be used right now anyway, though, so it'll be dependent on whoever uses it to figure out if this matters.
return false;
}
/// <summary>
/// Finds the access tags on the given entity
/// </summary>
/// <param name="uid">The entity that is being searched.</param>
/// <param name="items">All of the items to search for access. If none are passed in, <see cref="FindPotentialAccessItems"/> will be used.</param>
public ICollection<string> FindAccessTags(EntityUid uid, HashSet<EntityUid>? items = null)
{
HashSet<string>? tags = null;
var owned = false;
return reader.AccessLists.Count == 0 || reader.AccessLists.Any(a => a.IsSubsetOf(accessTags));
items ??= FindPotentialAccessItems(uid);
foreach (var ent in items)
{
FindAccessTagsItem(ent, ref tags, ref owned);
}
/// <summary>
/// Compares the given stationrecordkeys with the accessreader to see if it is allowed.
/// </summary>
public bool AreStationRecordKeysAllowed(ICollection<StationRecordKey> keys, AccessReaderComponent reader)
return (ICollection<string>?) tags ?? Array.Empty<string>();
}
/// <summary>
/// Finds the access tags on the given entity
/// </summary>
/// <param name="uid">The entity that is being searched.</param>
/// <param name="items">All of the items to search for access. If none are passed in, <see cref="FindPotentialAccessItems"/> will be used.</param>
public ICollection<StationRecordKey> FindStationRecordKeys(EntityUid uid, HashSet<EntityUid>? items = null)
{
HashSet<StationRecordKey> keys = new();
items ??= FindPotentialAccessItems(uid);
foreach (var ent in items)
{
return keys.Any() && reader.AccessKeys.Any(keys.Contains);
if (FindStationRecordKeyItem(ent, out var key))
keys.Add(key.Value);
}
/// <summary>
/// Finds all the items that could potentially give access to a given entity
/// </summary>
public HashSet<EntityUid> FindPotentialAccessItems(EntityUid uid)
{
FindAccessItemsInventory(uid, out var items);
return keys;
}
var ev = new GetAdditionalAccessEvent
{
Entities = items
};
RaiseLocalEvent(uid, ref ev);
items.Add(uid);
return items;
/// <summary>
/// Try to find <see cref="AccessComponent"/> on this item
/// or inside this item (if it's pda)
/// This version merges into a set or replaces the set.
/// If owned is false, the existing tag-set "isn't ours" and can't be merged with (is read-only).
/// </summary>
private void FindAccessTagsItem(EntityUid uid, ref HashSet<string>? tags, ref bool owned)
{
if (!FindAccessTagsItem(uid, out var targetTags))
{
// no tags, no problem
return;
}
/// <summary>
/// Finds the access tags on the given entity
/// </summary>
/// <param name="uid">The entity that is being searched.</param>
/// <param name="items">All of the items to search for access. If none are passed in, <see cref="FindPotentialAccessItems"/> will be used.</param>
public ICollection<string> FindAccessTags(EntityUid uid, HashSet<EntityUid>? items = null)
if (tags != null)
{
HashSet<string>? tags = null;
var owned = false;
items ??= FindPotentialAccessItems(uid);
foreach (var ent in items)
// existing tags, so copy to make sure we own them
if (!owned)
{
FindAccessTagsItem(ent, ref tags, ref owned);
tags = new(tags);
owned = true;
}
return (ICollection<string>?) tags ?? Array.Empty<string>();
// then merge
tags.UnionWith(targetTags);
}
/// <summary>
/// Finds the access tags on the given entity
/// </summary>
/// <param name="uid">The entity that is being searched.</param>
/// <param name="items">All of the items to search for access. If none are passed in, <see cref="FindPotentialAccessItems"/> will be used.</param>
public ICollection<StationRecordKey> FindStationRecordKeys(EntityUid uid, HashSet<EntityUid>? items = null)
else
{
HashSet<StationRecordKey> keys = new();
items ??= FindPotentialAccessItems(uid);
foreach (var ent in items)
{
if (FindStationRecordKeyItem(ent, out var key))
keys.Add(key.Value);
}
return keys;
}
/// <summary>
/// Try to find <see cref="AccessComponent"/> on this item
/// or inside this item (if it's pda)
/// This version merges into a set or replaces the set.
/// If owned is false, the existing tag-set "isn't ours" and can't be merged with (is read-only).
/// </summary>
private void FindAccessTagsItem(EntityUid uid, ref HashSet<string>? tags, ref bool owned)
{
if (!FindAccessTagsItem(uid, out var targetTags))
{
// no tags, no problem
return;
}
if (tags != null)
{
// existing tags, so copy to make sure we own them
if (!owned)
{
tags = new(tags);
owned = true;
}
// then merge
tags.UnionWith(targetTags);
}
else
{
// no existing tags, so now they're ours
tags = targetTags;
owned = false;
}
}
public bool FindAccessItemsInventory(EntityUid uid, out HashSet<EntityUid> items)
{
items = new();
foreach (var item in _handsSystem.EnumerateHeld(uid))
{
items.Add(item);
}
// maybe its inside an inventory slot?
if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid))
{
items.Add(idUid.Value);
}
return items.Any();
}
/// <summary>
/// Try to find <see cref="AccessComponent"/> on this item
/// or inside this item (if it's pda)
/// </summary>
private bool FindAccessTagsItem(EntityUid uid, [NotNullWhen(true)] out HashSet<string>? tags)
{
if (TryComp(uid, out AccessComponent? access))
{
tags = access.Tags;
return true;
}
if (TryComp(uid, out PdaComponent? pda) &&
pda.ContainedId?.Owner is {Valid: true} id)
{
tags = EntityManager.GetComponent<AccessComponent>(id).Tags;
return true;
}
tags = null;
return false;
}
/// <summary>
/// Try to find <see cref="StationRecordKeyStorageComponent"/> on this item
/// or inside this item (if it's pda)
/// </summary>
private bool FindStationRecordKeyItem(EntityUid uid, [NotNullWhen(true)] out StationRecordKey? key)
{
if (TryComp(uid, out StationRecordKeyStorageComponent? storage) && storage.Key != null)
{
key = storage.Key;
return true;
}
if (TryComp<PdaComponent>(uid, out var pda) &&
pda.ContainedId?.Owner is {Valid: true} id)
{
if (TryComp<StationRecordKeyStorageComponent>(id, out var pdastorage) && pdastorage.Key != null)
{
key = pdastorage.Key;
return true;
}
}
key = null;
return false;
// no existing tags, so now they're ours
tags = targetTags;
owned = false;
}
}
public bool FindAccessItemsInventory(EntityUid uid, out HashSet<EntityUid> items)
{
items = new();
foreach (var item in _handsSystem.EnumerateHeld(uid))
{
items.Add(item);
}
// maybe its inside an inventory slot?
if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid))
{
items.Add(idUid.Value);
}
return items.Any();
}
/// <summary>
/// Try to find <see cref="AccessComponent"/> on this item
/// or inside this item (if it's pda)
/// </summary>
private bool FindAccessTagsItem(EntityUid uid, [NotNullWhen(true)] out HashSet<string>? tags)
{
if (TryComp(uid, out AccessComponent? access))
{
tags = access.Tags;
return true;
}
if (TryComp(uid, out PdaComponent? pda) &&
pda.ContainedId is { Valid: true } id)
{
tags = EntityManager.GetComponent<AccessComponent>(id).Tags;
return true;
}
tags = null;
return false;
}
/// <summary>
/// Try to find <see cref="StationRecordKeyStorageComponent"/> on this item
/// or inside this item (if it's pda)
/// </summary>
private bool FindStationRecordKeyItem(EntityUid uid, [NotNullWhen(true)] out StationRecordKey? key)
{
if (TryComp(uid, out StationRecordKeyStorageComponent? storage) && storage.Key != null)
{
key = storage.Key;
return true;
}
if (TryComp<PdaComponent>(uid, out var pda) &&
pda.ContainedId is { Valid: true } id)
{
if (TryComp<StationRecordKeyStorageComponent>(id, out var pdastorage) && pdastorage.Key != null)
{
key = pdastorage.Key;
return true;
}
}
key = null;
return false;
}
}

View File

@@ -17,7 +17,7 @@ public abstract class SharedIdCardSystem : EntitySystem
public bool TryFindIdCard(EntityUid uid, [NotNullWhen(true)] out IdCardComponent? idCard)
{
// check held item?
if (EntityManager.TryGetComponent(uid, out HandsComponent? hands) &&
if (TryComp(uid, out HandsComponent? hands) &&
hands.ActiveHandEntity is EntityUid heldItem &&
TryGetIdCard(heldItem, out idCard))
{
@@ -30,9 +30,7 @@ public abstract class SharedIdCardSystem : EntitySystem
// check inventory slot?
if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid) && TryGetIdCard(idUid.Value, out idCard))
{
return true;
}
return false;
}
@@ -43,12 +41,12 @@ public abstract class SharedIdCardSystem : EntitySystem
/// </summary>
public bool TryGetIdCard(EntityUid uid, [NotNullWhen(true)] out IdCardComponent? idCard)
{
if (EntityManager.TryGetComponent(uid, out idCard))
if (TryComp(uid, out idCard))
return true;
if (EntityManager.TryGetComponent(uid, out PdaComponent? pda) && pda.ContainedId != null)
if (TryComp(uid, out PdaComponent? pda)
&& TryComp(pda.ContainedId, out idCard))
{
idCard = pda.ContainedId;
return true;
}

View File

@@ -77,13 +77,6 @@ namespace Content.Shared.Containers.ItemSlots
[DataField("ejectSound")]
public SoundSpecifier EjectSound = new SoundPathSpecifier("/Audio/Weapons/Guns/MagOut/revolver_magout.ogg");
/// <summary>
/// Options used for playing the insert/eject sounds.
/// </summary>
[DataField("soundOptions")]
[Obsolete("Use the sound specifer parameters instead")]
public AudioParams SoundOptions = AudioParams.Default;
/// <summary>
/// The name of this item slot. This will be shown to the user in the verb menu.
/// </summary>
@@ -228,7 +221,6 @@ namespace Content.Shared.Containers.ItemSlots
InsertSound = other.InsertSound;
EjectSound = other.EjectSound;
SoundOptions = other.SoundOptions;
Name = other.Name;
Locked = other.Locked;
InsertOnInteract = other.InsertOnInteract;

View File

@@ -65,7 +65,7 @@ namespace Content.Shared.Containers.ItemSlots
if (slot.HasItem || string.IsNullOrEmpty(slot.StartingItem))
continue;
var item = EntityManager.SpawnEntity(slot.StartingItem, EntityManager.GetComponent<TransformComponent>(itemSlots.Owner).Coordinates);
var item = EntityManager.SpawnEntity(slot.StartingItem, EntityManager.GetComponent<TransformComponent>(uid).Coordinates);
slot.ContainerSlot?.Insert(item);
}
}
@@ -77,7 +77,7 @@ namespace Content.Shared.Containers.ItemSlots
{
foreach (var (id, slot) in itemSlots.Slots)
{
slot.ContainerSlot = _containers.EnsureContainer<ContainerSlot>(itemSlots.Owner, id);
slot.ContainerSlot = _containers.EnsureContainer<ContainerSlot>(uid, id);
}
}
@@ -93,13 +93,13 @@ namespace Content.Shared.Containers.ItemSlots
if (itemSlots.Slots.TryGetValue(id, out var existing))
{
if (existing.Local)
Logger.Error($"Duplicate item slot key. Entity: {EntityManager.GetComponent<MetaDataComponent>(itemSlots.Owner).EntityName} ({uid}), key: {id}");
Log.Error($"Duplicate item slot key. Entity: {EntityManager.GetComponent<MetaDataComponent>(uid).EntityName} ({uid}), key: {id}");
else
// server state takes priority
slot.CopyFrom(existing);
}
slot.ContainerSlot = _containers.EnsureContainer<ContainerSlot>(itemSlots.Owner, id);
slot.ContainerSlot = _containers.EnsureContainer<ContainerSlot>(uid, id);
itemSlots.Slots[id] = slot;
Dirty(itemSlots);
}
@@ -340,7 +340,7 @@ namespace Content.Shared.Containers.ItemSlots
if (ejected != null && ejected.Value && user != null)
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user.Value)} ejected {ToPrettyString(item)} from {slot.ContainerSlot?.ID + " slot of "}{ToPrettyString(uid)}");
_audioSystem.PlayPredicted(slot.EjectSound, uid, excludeUserAudio ? user : null, slot.SoundOptions);
_audioSystem.PlayPredicted(slot.EjectSound, uid, excludeUserAudio ? user : null);
}
/// <summary>
@@ -477,9 +477,11 @@ namespace Content.Shared.Containers.ItemSlots
? Loc.GetString(slot.Name)
: EntityManager.GetComponent<MetaDataComponent>(slot.Item.Value).EntityName ?? string.Empty;
AlternativeVerb verb = new();
verb.IconEntity = slot.Item;
verb.Act = () => TryEjectToHands(uid, slot, args.User, excludeUserAudio: true);
AlternativeVerb verb = new()
{
IconEntity = slot.Item,
Act = () => TryEjectToHands(uid, slot, args.User, excludeUserAudio: true)
};
if (slot.EjectVerbText == null)
{
@@ -538,18 +540,20 @@ namespace Content.Shared.Containers.ItemSlots
var verbSubject = slot.Name != string.Empty
? Loc.GetString(slot.Name)
: Name(args.Using.Value) ?? string.Empty;
: Name(args.Using.Value);
InteractionVerb insertVerb = new();
insertVerb.IconEntity = args.Using;
insertVerb.Act = () => Insert(uid, slot, args.Using.Value, args.User, excludeUserAudio: true);
InteractionVerb insertVerb = new()
{
IconEntity = args.Using,
Act = () => Insert(uid, slot, args.Using.Value, args.User, excludeUserAudio: true)
};
if (slot.InsertVerbText != null)
{
insertVerb.Text = Loc.GetString(slot.InsertVerbText);
insertVerb.Icon =
new SpriteSpecifier.Texture(
new("/Textures/Interface/VerbIcons/insert.svg.192dpi.png"));
new ResPath("/Textures/Interface/VerbIcons/insert.svg.192dpi.png"));
}
else if(slot.EjectOnInteract)
{
@@ -558,7 +562,7 @@ namespace Content.Shared.Containers.ItemSlots
insertVerb.Text = Loc.GetString("place-item-verb-text", ("subject", verbSubject));
insertVerb.Icon =
new SpriteSpecifier.Texture(
new("/Textures/Interface/VerbIcons/drop.svg.192dpi.png"));
new ResPath("/Textures/Interface/VerbIcons/drop.svg.192dpi.png"));
}
else
{
@@ -635,7 +639,7 @@ namespace Content.Shared.Containers.ItemSlots
return;
slot.Locked = locked;
itemSlots.Dirty();
Dirty(uid, itemSlots);
}
/// <summary>

View File

@@ -39,18 +39,6 @@ public sealed class CuffableComponent : Component
/// </summary>
[DataField("canStillInteract"), ViewVariables(VVAccess.ReadWrite)]
public bool CanStillInteract = true;
/// <summary>
/// Damage is applied to someone when they try to uncuff themselves.
/// </summary>
[DataField("damageOnResist"), ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier DamageOnResist = new()
{
DamageDict = new()
{
{ "Blunt", 3.0 },
}
};
}
[Serializable, NetSerializable]

View File

@@ -1,3 +1,4 @@
using Content.Shared.Damage;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
@@ -47,6 +48,15 @@ public sealed class HandcuffComponent : Component
[DataField("brokenPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>)), ViewVariables(VVAccess.ReadWrite)]
public string? BrokenPrototype;
[DataField("damageOnResist"), ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier DamageOnResist = new()
{
DamageDict = new()
{
{ "Blunt", 3.0 },
}
};
/// <summary>
/// The path of the RSI file used for the player cuffed overlay.
/// </summary>

View File

@@ -266,7 +266,7 @@ namespace Content.Shared.Cuffs
private void OnCuffAfterInteract(EntityUid uid, HandcuffComponent component, AfterInteractEvent args)
{
if (args.Target is not {Valid: true} target)
if (args.Target is not { Valid: true } target)
return;
if (!args.CanReach)
@@ -580,7 +580,7 @@ namespace Content.Shared.Cuffs
if (isOwner)
{
_damageSystem.TryChangeDamage(target, cuffable.DamageOnResist, true, false);
_damageSystem.TryChangeDamage(target, cuff.DamageOnResist, true, false);
}
if (_net.IsServer)

View File

@@ -23,14 +23,14 @@ public abstract class SharedDoorSystem : EntitySystem
{
[Dependency] protected readonly IGameTiming GameTiming = default!;
[Dependency] protected readonly SharedPhysicsSystem PhysicsSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] protected readonly TagSystem Tags = default!;
[Dependency] protected readonly SharedAudioSystem Audio = default!;
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
[Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!;
[Dependency] private readonly OccluderSystem _occluder = default!;
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
[Dependency] private readonly OccluderSystem _occluder = default!;
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
/// <summary>
/// A body must have an intersection percentage larger than this in order to be considered as colliding with a

View File

@@ -8,7 +8,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
namespace Content.Shared.Implants.Components;
/// <summary>
/// Implanters are used to implant or extract implants from an entity
/// Implanters are used to implant or extract implants from an entity.
/// Some can be single use (implant only) or some can draw out an implant
/// </summary>
//TODO: Rework drawing to work with implant cases when surgery is in

View File

@@ -59,9 +59,9 @@ public abstract class SharedImplanterSystem : EntitySystem
implantContainer.Insert(implant.Value);
if (component.CurrentMode == ImplanterToggleMode.Inject && !component.ImplantOnly)
DrawMode(component);
DrawMode(implanter, component);
else
ImplantMode(component);
ImplantMode(implanter, component);
Dirty(component);
}
@@ -74,8 +74,8 @@ public abstract class SharedImplanterSystem : EntitySystem
[NotNullWhen(true)] out EntityUid? implant,
[NotNullWhen(true)] out SubdermalImplantComponent? implantComp)
{
implant = component.ImplanterSlot.ContainerSlot?.ContainedEntities?.FirstOrDefault();
if (!TryComp<SubdermalImplantComponent>(implant, out implantComp))
implant = component.ImplanterSlot.ContainerSlot?.ContainedEntities.FirstOrDefault();
if (!TryComp(implant, out implantComp))
return false;
var ev = new AddImplantAttemptEvent(user, target, implant.Value, implanter);
@@ -101,7 +101,7 @@ public abstract class SharedImplanterSystem : EntitySystem
foreach (var implant in implantContainer.ContainedEntities)
{
if (!implantCompQuery.TryGetComponent(implant, out var implantComp))
return;
continue;
//Don't remove a permanent implant and look for the next that can be drawn
if (!implantContainer.CanRemove(implant))
@@ -124,27 +124,27 @@ public abstract class SharedImplanterSystem : EntitySystem
}
if (component.CurrentMode == ImplanterToggleMode.Draw && !component.ImplantOnly && !permanentFound)
ImplantMode(component);
ImplantMode(implanter, component);
Dirty(component);
}
}
private void ImplantMode(ImplanterComponent component)
private void ImplantMode(EntityUid uid, ImplanterComponent component)
{
component.CurrentMode = ImplanterToggleMode.Inject;
ChangeOnImplantVisualizer(component);
ChangeOnImplantVisualizer(uid, component);
}
private void DrawMode(ImplanterComponent component)
private void DrawMode(EntityUid uid, ImplanterComponent component)
{
component.CurrentMode = ImplanterToggleMode.Draw;
ChangeOnImplantVisualizer(component);
ChangeOnImplantVisualizer(uid, component);
}
private void ChangeOnImplantVisualizer(ImplanterComponent component)
private void ChangeOnImplantVisualizer(EntityUid uid, ImplanterComponent component)
{
if (!TryComp<AppearanceComponent>(component.Owner, out var appearance))
if (!TryComp<AppearanceComponent>(uid, out var appearance))
return;
bool implantFound;
@@ -156,17 +156,17 @@ public abstract class SharedImplanterSystem : EntitySystem
implantFound = false;
if (component.CurrentMode == ImplanterToggleMode.Inject && !component.ImplantOnly)
_appearance.SetData(component.Owner, ImplanterVisuals.Full, implantFound, appearance);
_appearance.SetData(uid, ImplanterVisuals.Full, implantFound, appearance);
else if (component.CurrentMode == ImplanterToggleMode.Inject && component.ImplantOnly)
{
_appearance.SetData(component.Owner, ImplanterVisuals.Full, implantFound, appearance);
_appearance.SetData(component.Owner, ImplanterImplantOnlyVisuals.ImplantOnly, component.ImplantOnly,
_appearance.SetData(uid, ImplanterVisuals.Full, implantFound, appearance);
_appearance.SetData(uid, ImplanterImplantOnlyVisuals.ImplantOnly, component.ImplantOnly,
appearance);
}
else
_appearance.SetData(component.Owner, ImplanterVisuals.Full, implantFound, appearance);
_appearance.SetData(uid, ImplanterVisuals.Full, implantFound, appearance);
}
}

View File

@@ -6,20 +6,17 @@ using Content.Shared.Movement.Events;
using Content.Shared.Popups;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
namespace Content.Shared.Movement.Systems;
public abstract class SharedJetpackSystem : EntitySystem
{
[Dependency] protected readonly MovementSpeedModifierSystem MovementSpeedModifier = default!;
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
[Dependency] protected readonly SharedContainerSystem Container = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedMoverController _mover = default!;
[Dependency] private readonly SharedMoverController _mover = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
public override void Initialize()
{
@@ -55,14 +52,16 @@ public abstract class SharedJetpackSystem : EntitySystem
{
_popup.PopupClient(Loc.GetString("jetpack-to-grid"), uid, uid);
SetEnabled(jetpack, false, uid);
SetEnabled(user.Jetpack, jetpack, false, uid);
}
}
}
private void OnJetpackUserHandleState(EntityUid uid, JetpackUserComponent component, ref ComponentHandleState args)
{
if (args.Current is not JetpackUserComponentState state) return;
if (args.Current is not JetpackUserComponentState state)
return;
component.Jetpack = state.Jetpack;
}
@@ -76,7 +75,7 @@ public abstract class SharedJetpackSystem : EntitySystem
private void OnJetpackDropped(EntityUid uid, JetpackComponent component, DroppedEvent args)
{
SetEnabled(component, false, args.User);
SetEnabled(uid, component, false, args.User);
}
private void OnJetpackUserCanWeightless(EntityUid uid, JetpackUserComponent component, ref CanWeightlessMoveEvent args)
@@ -89,22 +88,24 @@ public abstract class SharedJetpackSystem : EntitySystem
if (TryComp<JetpackComponent>(component.Jetpack, out var jetpack) &&
!CanEnableOnGrid(args.Transform.GridUid))
{
SetEnabled(jetpack, false, uid);
SetEnabled(component.Jetpack, jetpack, false, uid);
_popup.PopupClient(Loc.GetString("jetpack-to-grid"), uid, uid);
}
}
private void SetupUser(EntityUid uid, JetpackComponent component)
private void SetupUser(EntityUid user, EntityUid jetpackUid)
{
var user = EnsureComp<JetpackUserComponent>(uid);
_mover.SetRelay(uid, component.Owner);
user.Jetpack = component.Owner;
var userComp = EnsureComp<JetpackUserComponent>(user);
_mover.SetRelay(user, jetpackUid);
userComp.Jetpack = jetpackUid;
}
private void RemoveUser(EntityUid uid)
{
if (!RemComp<JetpackUserComponent>(uid)) return;
if (!RemComp<JetpackUserComponent>(uid))
return;
RemComp<RelayInputMoverComponent>(uid);
}
@@ -120,7 +121,7 @@ public abstract class SharedJetpackSystem : EntitySystem
return;
}
SetEnabled(component, !IsEnabled(uid));
SetEnabled(uid, component, !IsEnabled(uid));
}
private bool CanEnableOnGrid(EntityUid? gridUid)
@@ -139,44 +140,48 @@ public abstract class SharedJetpackSystem : EntitySystem
return HasComp<ActiveJetpackComponent>(uid);
}
public void SetEnabled(JetpackComponent component, bool enabled, EntityUid? user = null)
public void SetEnabled(EntityUid uid, JetpackComponent component, bool enabled, EntityUid? user = null)
{
if (IsEnabled(component.Owner) == enabled ||
enabled && !CanEnable(component)) return;
if (IsEnabled(uid) == enabled ||
enabled && !CanEnable(uid, component))
{
return;
}
if (enabled)
{
EnsureComp<ActiveJetpackComponent>(component.Owner);
EnsureComp<ActiveJetpackComponent>(uid);
}
else
{
RemComp<ActiveJetpackComponent>(component.Owner);
RemComp<ActiveJetpackComponent>(uid);
}
if (user == null)
{
Container.TryGetContainingContainer(component.Owner, out var container);
Container.TryGetContainingContainer(uid, out var container);
user = container?.Owner;
}
// Can't activate if no one's using.
if (user == null && enabled) return;
if (user == null && enabled)
return;
if (user != null)
{
if (enabled)
{
SetupUser(user.Value, component);
SetupUser(user.Value, uid);
}
else
{
RemoveUser(user.Value);
}
MovementSpeedModifier.RefreshMovementSpeedModifiers(user.Value);
_movementSpeedModifier.RefreshMovementSpeedModifiers(user.Value);
}
Appearance.SetData(component.Owner, JetpackVisuals.Enabled, enabled);
Appearance.SetData(uid, JetpackVisuals.Enabled, enabled);
Dirty(component);
}
@@ -185,7 +190,7 @@ public abstract class SharedJetpackSystem : EntitySystem
return HasComp<JetpackUserComponent>(uid);
}
protected virtual bool CanEnable(JetpackComponent component)
protected virtual bool CanEnable(EntityUid uid, JetpackComponent component)
{
return true;
}

View File

@@ -30,7 +30,7 @@ namespace Content.Shared.PDA
[DataField("id", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? IdCard;
[ViewVariables] public IdCardComponent? ContainedId;
[ViewVariables] public EntityUid? ContainedId;
[ViewVariables] public bool FlashlightOn;
[ViewVariables] public string? OwnerName;

View File

@@ -40,7 +40,7 @@ namespace Content.Shared.PDA
protected virtual void OnItemInserted(EntityUid uid, PdaComponent pda, EntInsertedIntoContainerMessage args)
{
if (args.Container.ID == PdaComponent.PdaIdSlotId)
pda.ContainedId = CompOrNull<IdCardComponent>(args.Entity);
pda.ContainedId = args.Entity;
UpdatePdaAppearance(uid, pda);
}

View File

@@ -1,6 +1,4 @@
using Content.Shared.StepTrigger.Components;
using Content.Shared.Tag;
using Robust.Shared.Collections;
using Robust.Shared.GameStates;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
@@ -74,26 +72,26 @@ public sealed class StepTriggerSystem : EntitySystem
foreach (var otherUid in component.Colliding)
{
UpdateColliding(component, transform, otherUid, query);
UpdateColliding(uid, component, transform, otherUid, query);
}
return false;
}
private void UpdateColliding(StepTriggerComponent component, TransformComponent ownerTransform, EntityUid otherUid, EntityQuery<PhysicsComponent> query)
private void UpdateColliding(EntityUid uid, StepTriggerComponent component, TransformComponent ownerTransform, EntityUid otherUid, EntityQuery<PhysicsComponent> query)
{
if (!query.TryGetComponent(otherUid, out var otherPhysics))
return;
// TODO: This shouldn't be calculating based on world AABBs.
var ourAabb = _entityLookup.GetWorldAABB(component.Owner, ownerTransform);
var ourAabb = _entityLookup.GetWorldAABB(uid, ownerTransform);
var otherAabb = _entityLookup.GetWorldAABB(otherUid);
if (!ourAabb.Intersects(otherAabb))
{
if (component.CurrentlySteppedOn.Remove(otherUid))
{
Dirty(component);
Dirty(uid, component);
}
return;
}
@@ -101,17 +99,16 @@ public sealed class StepTriggerSystem : EntitySystem
if (otherPhysics.LinearVelocity.Length() < component.RequiredTriggerSpeed
|| component.CurrentlySteppedOn.Contains(otherUid)
|| otherAabb.IntersectPercentage(ourAabb) < component.IntersectRatio
|| !CanTrigger(component.Owner, otherUid, component))
|| !CanTrigger(uid, otherUid, component))
{
return;
}
var ev = new StepTriggeredEvent { Source = component.Owner, Tripper = otherUid };
RaiseLocalEvent(component.Owner, ref ev, true);
var ev = new StepTriggeredEvent { Source = uid, Tripper = otherUid };
RaiseLocalEvent(uid, ref ev, true);
component.CurrentlySteppedOn.Add(otherUid);
Dirty(component);
return;
Dirty(uid, component);
}
private bool CanTrigger(EntityUid uid, EntityUid otherUid, StepTriggerComponent component)
@@ -140,7 +137,7 @@ public sealed class StepTriggerSystem : EntitySystem
if (component.Colliding.Add(otherUid))
{
Dirty(component);
Dirty(uid, component);
}
}
@@ -152,7 +149,7 @@ public sealed class StepTriggerSystem : EntitySystem
return;
component.CurrentlySteppedOn.Remove(otherUid);
Dirty(component);
Dirty(uid, component);
if (component.Colliding.Count == 0)
{
@@ -211,7 +208,7 @@ public sealed class StepTriggerSystem : EntitySystem
return;
component.IntersectRatio = ratio;
Dirty(component);
Dirty(uid, component);
}
public void SetRequiredTriggerSpeed(EntityUid uid, float speed, StepTriggerComponent? component = null)
@@ -223,7 +220,7 @@ public sealed class StepTriggerSystem : EntitySystem
return;
component.RequiredTriggerSpeed = speed;
Dirty(component);
Dirty(uid, component);
}
public void SetActive(EntityUid uid, bool active, StepTriggerComponent? component = null)
@@ -235,7 +232,7 @@ public sealed class StepTriggerSystem : EntitySystem
return;
component.Active = active;
Dirty(component);
Dirty(uid, component);
}
}

View File

@@ -21,32 +21,32 @@ namespace Content.Shared.Storage.EntitySystems
private void CounterEntityInserted(EntityUid uid, ItemCounterComponent itemCounter,
EntInsertedIntoContainerMessage args)
{
if (!EntityManager.TryGetComponent(itemCounter.Owner, out AppearanceComponent? appearanceComponent))
if (!EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent))
return;
var count = GetCount(args, itemCounter);
if (count == null)
return;
_appearance.SetData(itemCounter.Owner, StackVisuals.Actual, count, appearanceComponent);
_appearance.SetData(uid, StackVisuals.Actual, count, appearanceComponent);
if (itemCounter.MaxAmount != null)
_appearance.SetData(itemCounter.Owner, StackVisuals.MaxCount, itemCounter.MaxAmount, appearanceComponent);
_appearance.SetData(uid, StackVisuals.MaxCount, itemCounter.MaxAmount, appearanceComponent);
}
private void CounterEntityRemoved(EntityUid uid, ItemCounterComponent itemCounter,
EntRemovedFromContainerMessage args)
{
if (!EntityManager.TryGetComponent(itemCounter.Owner, out AppearanceComponent? appearanceComponent))
if (!EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent))
return;
var count = GetCount(args, itemCounter);
if (count == null)
return;
_appearance.SetData(itemCounter.Owner, StackVisuals.Actual, count, appearanceComponent);
_appearance.SetData(uid, StackVisuals.Actual, count, appearanceComponent);
if (itemCounter.MaxAmount != null)
_appearance.SetData(itemCounter.Owner, StackVisuals.MaxCount, itemCounter.MaxAmount, appearanceComponent);
_appearance.SetData(uid, StackVisuals.MaxCount, itemCounter.MaxAmount, appearanceComponent);
}
protected abstract int? GetCount(ContainerModifiedMessage msg, ItemCounterComponent itemCounter);

View File

@@ -78,7 +78,7 @@ public abstract partial class SharedGunSystem
Audio.PlayPredicted(component.SoundModeToggle, uid, user);
Popup(Loc.GetString("gun-selected-mode", ("mode", GetLocSelector(fire))), uid, user);
Dirty(component);
Dirty(uid, component);
}
/// <summary>

View File

@@ -8,6 +8,7 @@ using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using System;
using System.Linq;
using JetBrains.Annotations;
namespace Content.Shared.Weapons.Ranged.Systems;
@@ -164,7 +165,7 @@ public partial class SharedGunSystem
Popup(Loc.GetString("gun-revolver-insert"), revolverUid, user);
UpdateRevolverAppearance(revolverUid, component);
UpdateAmmoCount(uid);
Dirty(component);
Dirty(uid, component);
return true;
}
@@ -223,6 +224,7 @@ public partial class SharedGunSystem
return count;
}
[PublicAPI]
private int GetRevolverUnspentCount(RevolverAmmoProviderComponent component)
{
var count = 0;
@@ -261,7 +263,8 @@ public partial class SharedGunSystem
if (slot == null)
{
if (chamber == null) continue;
if (chamber == null)
continue;
// Too lazy to make a new method don't sue me.
if (!_netManager.IsClient)
@@ -294,7 +297,7 @@ public partial class SharedGunSystem
Audio.PlayPredicted(component.SoundEject, revolverUid, user);
UpdateAmmoCount(revolverUid);
UpdateRevolverAppearance(revolverUid, component);
Dirty(component);
Dirty(revolverUid, component);
}
}
@@ -366,12 +369,12 @@ public partial class SharedGunSystem
component.AmmoContainer.Remove(ent.Value);
component.AmmoSlots[index] = null;
args.Ammo.Add((ent.Value, EnsureComp<AmmoComponent>(ent.Value)));
Transform(ent.Value).Coordinates = args.Coordinates;
TransformSystem.SetCoordinates(ent.Value, args.Coordinates);
}
}
UpdateRevolverAppearance(uid, component);
Dirty(component);
Dirty(uid, component);
}
private void Cycle(RevolverAmmoProviderComponent component, int count = 1)

View File

@@ -329,7 +329,7 @@ public abstract partial class SharedGunSystem : EntitySystem
CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics);
}
Dirty(gun);
Dirty(gunUid, gun);
}
public void Shoot(
@@ -366,7 +366,7 @@ public abstract partial class SharedGunSystem : EntitySystem
protected void SetCartridgeSpent(EntityUid uid, CartridgeAmmoComponent cartridge, bool spent)
{
if (cartridge.Spent != spent)
Dirty(cartridge);
Dirty(uid, cartridge);
cartridge.Spent = spent;
Appearance.SetData(uid, AmmoVisuals.Spent, spent);
@@ -380,14 +380,14 @@ public abstract partial class SharedGunSystem : EntitySystem
bool playSound = true)
{
// TODO: Sound limit version.
var offsetPos = (Random.NextVector2(EjectOffset));
var offsetPos = Random.NextVector2(EjectOffset);
var xform = Transform(entity);
var coordinates = xform.Coordinates;
coordinates = coordinates.Offset(offsetPos);
xform.LocalRotation = Random.NextAngle();
xform.Coordinates = coordinates;
TransformSystem.SetLocalRotation(xform, Random.NextAngle());
TransformSystem.SetCoordinates(entity, xform, coordinates);
if (playSound && TryComp<CartridgeAmmoComponent>(entity, out var cartridge))
{

View File

@@ -1,4 +1,4 @@
- files: ["nukeops_start.ogg"]
- files: ["nukeops_start.ogg, traitor_start.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken from TG station"
source: "https://github.com/tgstation/tgstation/commit/827967c9650c23af64280ad1491405fed8f644c5#diff-6cc910b7cad9ac4333c8d0885fc844746066120b465d8d4ba8f7019169316574"

View File

@@ -1,41 +1,4 @@
Entries:
- author: VigersRay
changes:
- {message: 'Added alert level, instruction and shift duration in PDA!', type: Add}
- {message: Fix uknown station name in PDA for latejoin!, type: Fix}
id: 3801
time: '2023-05-17T20:35:41.0000000+00:00'
- author: metalgearsloth
changes:
- {message: Add lathe recipe for tethergun., type: Add}
id: 3802
time: '2023-05-18T02:07:11.0000000+00:00'
- author: DrSmugleaf
changes:
- {message: Fixed Next on the admin logs UI resending the same logs., type: Fix}
id: 3803
time: '2023-05-18T05:51:17.0000000+00:00'
- author: FluffiestFloof
changes:
- {message: Updated the Suit Storage look., type: Tweak}
id: 3804
time: '2023-05-18T17:52:05.0000000+00:00'
- author: EmoGarbage404
changes:
- {message: Vending machines can now be completely destroyed., type: Fix}
id: 3805
time: '2023-05-18T23:48:14.0000000+00:00'
- author: themias
changes:
- {message: Mobs that are in critical condition no longer appear as dead on the
crew monitor, type: Fix}
id: 3806
time: '2023-05-18T23:49:55.0000000+00:00'
- author: OctoRocket
changes:
- {message: 'Antifreeze tastes warm now, because it is antifreeze.', type: Tweak}
id: 3807
time: '2023-05-19T02:21:57.0000000+00:00'
- author: metalgearsloth
changes:
- {message: Disabled zombies preset temporarily until they have more work done.,
@@ -2983,3 +2946,40 @@ Entries:
- {message: new and improved ban panel, type: Add}
id: 4300
time: '2023-07-21T11:38:53.0000000+00:00'
- author: Errant
changes:
- {message: Nitrogen tank settings have been adjusted to avoid gas wastage, type: Tweak}
id: 4301
time: '2023-07-21T19:02:39.0000000+00:00'
- author: JimGamemaster
changes:
- {message: Space Creatures can now float in space, type: Tweak}
id: 4302
time: '2023-07-21T20:10:21.0000000+00:00'
- author: brainfood1183
changes:
- {message: Strait Jackets can now be used to restrain individuals., type: Add}
id: 4303
time: '2023-07-22T22:14:25.0000000+00:00'
- author: Emisse
changes:
- {message: Beds and medical beds now heal blunt and cold specifically instead of
brute and burn groups., type: Tweak}
id: 4304
time: '2023-07-23T03:18:06.0000000+00:00'
- author: EmoGarbage404
changes:
- {message: Midround anomaly spawns no longer have a high chance of spawning in
escape pods., type: Fix}
id: 4305
time: '2023-07-23T03:57:13.0000000+00:00'
- author: FillerVK
changes:
- {message: Departure Terminal update., type: Tweak}
id: 4306
time: '2023-07-23T05:32:04.0000000+00:00'
- author: Flareguy
changes:
- {message: Changed the roundstart traitor sound effect back to the old one., type: Tweak}
id: 4307
time: '2023-07-23T05:53:01.0000000+00:00'

View File

@@ -1,17 +1,17 @@
cuffable-component-cannot-interact-message = You can't do that!
cuffable-component-cannot-remove-cuffs-too-far-message = You are too far away to remove the cuffs.
cuffable-component-cannot-remove-cuffs-too-far-message = You are too far away to remove the restraints.
cuffable-component-start-uncuffing-self = You start to painfully wriggle out of your cuffs.
cuffable-component-start-uncuffing-observer = {$user} starts uncuffing {$target}!
cuffable-component-start-uncuffing-target-message = You start uncuffing {$targetName}.
cuffable-component-start-uncuffing-by-other-message = {$otherName} starts uncuffing you!
cuffable-component-start-uncuffing-self = You start to painfully wriggle out of your restraints.
cuffable-component-start-uncuffing-observer = {$user} starts unrestraining {$target}!
cuffable-component-start-uncuffing-target-message = You start unrestraining {$targetName}.
cuffable-component-start-uncuffing-by-other-message = {$otherName} starts unrestraining you!
cuffable-component-remove-cuffs-success-message = You successfully remove the cuffs.
cuffable-component-remove-cuffs-by-other-success-message = {$otherName} uncuffs your hands.
cuffable-component-remove-cuffs-to-other-partial-success-message = You successfully remove the cuffs. {$cuffedHandCount} of {$otherName}'s hands remain cuffed.
cuffable-component-remove-cuffs-by-other-partial-success-message = {$otherName} removes your cuffs. {$cuffedHandCount} of your hands remain cuffed.
cuffable-component-remove-cuffs-partial-success-message = You successfully remove the cuffs. {$cuffedHandCount} of your hands remain cuffed.
cuffable-component-remove-cuffs-fail-message = You fail to remove the cuffs.
cuffable-component-remove-cuffs-success-message = You successfully remove the restraints.
cuffable-component-remove-cuffs-by-other-success-message = {$otherName} unrestrains your hands.
cuffable-component-remove-cuffs-to-other-partial-success-message = You successfully remove the restraints. {$cuffedHandCount} of {$otherName}'s hands remain restrained.
cuffable-component-remove-cuffs-by-other-partial-success-message = {$otherName} removes your restraints. {$cuffedHandCount} of your hands remain restrained.
cuffable-component-remove-cuffs-partial-success-message = You successfully remove the restraints. {$cuffedHandCount} of your hands remain restrained.
cuffable-component-remove-cuffs-fail-message = You fail to remove the restraints.
# UncuffVerb
uncuff-verb-get-data-text = Uncuff
# UnrestrainVerb
uncuff-verb-get-data-text = Unrestrain

View File

@@ -1,17 +1,17 @@
handcuff-component-target-self = You start cuffing yourself.
handcuff-component-cuffs-broken-error = The cuffs are broken!
handcuff-component-target-self = You start restraining yourself.
handcuff-component-cuffs-broken-error = The restraints are broken!
handcuff-component-target-has-no-hands-error = {$targetName} has no hands!
handcuff-component-target-has-no-free-hands-error = {$targetName} has no free hands!
handcuff-component-too-far-away-error = You are too far away to use the cuffs!
handcuff-component-start-cuffing-observer = {$user} starts cuffing {$target}!
handcuff-component-start-cuffing-target-message = You start cuffing {$targetName}.
handcuff-component-start-cuffing-by-other-message = {$otherName} starts cuffing you!
handcuff-component-cuff-observer-success-message = {$user} cuffs {$target}.
handcuff-component-cuff-other-success-message = You successfully cuff {$otherName}.
handcuff-component-cuff-by-other-success-message = You have been cuffed by {$otherName}!
handcuff-component-cuff-self-success-message = You cuff yourself.
handcuff-component-cuff-interrupt-message = You were interrupted while cuffing {$targetName}!
handcuff-component-cuff-interrupt-other-message = You interrupt {$otherName} while they are cuffing you!
handcuff-component-cuff-interrupt-self-message = You were interrupted while cuffing yourself.
handcuff-component-cuff-interrupt-buckled-message = You can't buckle while cuffed!
handcuff-component-cuff-interrupt-unbuckled-message = You can't unbuckle while cuffed!
handcuff-component-too-far-away-error = You are too far away to use the restraints!
handcuff-component-start-cuffing-observer = {$user} starts restraining {$target}!
handcuff-component-start-cuffing-target-message = You start restraining {$targetName}.
handcuff-component-start-cuffing-by-other-message = {$otherName} starts restraining you!
handcuff-component-cuff-observer-success-message = {$user} restrains {$target}.
handcuff-component-cuff-other-success-message = You successfully restrain {$otherName}.
handcuff-component-cuff-by-other-success-message = You have been restrained by {$otherName}!
handcuff-component-cuff-self-success-message = You restrain yourself.
handcuff-component-cuff-interrupt-message = You were interrupted while unrestraining {$targetName}!
handcuff-component-cuff-interrupt-other-message = You interrupt {$otherName} while they are restraining you!
handcuff-component-cuff-interrupt-self-message = You were interrupted while unrestraining yourself.
handcuff-component-cuff-interrupt-buckled-message = You can't buckle while restrained!
handcuff-component-cuff-interrupt-unbuckled-message = You can't unbuckle while restrained!

File diff suppressed because it is too large Load Diff

View File

@@ -87,7 +87,7 @@
name: nitrogen tank
components:
- type: GasTank
outputPressure: 101.325
outputPressure: 21.27825
air:
volume: 15
moles:

View File

@@ -128,7 +128,7 @@
- id: WeaponRevolverInspector
- id: DrinkDetFlask
- id: SpeedLoaderMagnum
- id: ForensicGloves
- id: ClothingHandsGlovesForensic
- type: entity
id: ClosetBombFilled

View File

@@ -343,7 +343,7 @@
- type: entity
parent: ClothingHandsBase
id: ForensicGloves
id: ClothingHandsGlovesForensic
name: forensic gloves
description: Do not leave fibers or fingerprints. If you work without them, you're A TERRIBLE DETECTIVE.
components:
@@ -352,4 +352,3 @@
- type: Clothing
sprite: Clothing/Hands/Gloves/forensic.rsi
- type: FingerprintMask

View File

@@ -145,17 +145,6 @@
- type: Clothing
sprite: Clothing/OuterClothing/Misc/santa.rsi
- type: entity
parent: ClothingOuterBase
id: ClothingOuterStraightjacket
name: straight jacket
description: A straight jacket.
components:
- type: Sprite
sprite: Clothing/OuterClothing/Misc/straight_jacket.rsi
- type: Clothing
sprite: Clothing/OuterClothing/Misc/straight_jacket.rsi
# Is this wizard wearing a fanny pack???
- type: entity
parent: ClothingOuterBase

View File

@@ -36,6 +36,7 @@
150: Dead
- type: Stamina
excess: 150
- type: MovementAlwaysTouching
- type: Bloodstream
bloodMaxVolume: 300
bloodReagent: Cryoxadone

View File

@@ -120,11 +120,6 @@
- map: ["enum.HumanoidVisualLayers.RFoot"]
- map: ["enum.HumanoidVisualLayers.LHand"]
- map: ["enum.HumanoidVisualLayers.RHand"]
- map: ["enum.HumanoidVisualLayers.Handcuffs"]
color: "#ffffff"
sprite: Objects/Misc/handcuffs.rsi
state: body-overlay-2
visible: false
- map: [ "id" ]
- map: [ "gloves" ]
- map: [ "shoes" ]
@@ -143,6 +138,11 @@
- map: [ "head" ]
- map: [ "pocket1" ]
- map: [ "pocket2" ]
- map: ["enum.HumanoidVisualLayers.Handcuffs"]
color: "#ffffff"
sprite: Objects/Misc/handcuffs.rsi
state: body-overlay-2
visible: false
- map: [ "clownedon" ] # Dynamically generated
sprite: "Effects/creampie.rsi"
state: "creampie_human"

View File

@@ -117,3 +117,24 @@
sprite: Objects/Misc/cablecuffs.rsi
state: cuff-broken
color: forestgreen
- type: entity
parent: Handcuffs
id: ClothingOuterStraightjacket
name: straitjacket
description: Used to restrain those who may cause harm to themselves or others.
components:
- type: Item
size: 20
- type: Handcuff
cuffedRSI: Clothing/OuterClothing/Misc/straight_jacket.rsi
breakoutTime: 100
damageOnResist:
types:
Blunt: 0
cuffTime: 10
uncuffTime: 10
stunBonus: 4
- type: Sprite
sprite: Clothing/OuterClothing/Misc/straight_jacket.rsi
state: icon

View File

@@ -33,6 +33,7 @@
map: ["enum.GunVisualLayers.Base"]
- type: Clothing
sprite: Objects/Weapons/Guns/Launchers/china_lake.rsi
- type: AmmoCounter
- type: Gun
fireRate: 1
selectedMode: SemiAuto

View File

@@ -66,6 +66,7 @@
slots:
- Back
- suitStorage
- type: AmmoCounter
- type: Gun
fireRate: 2
selectedMode: FullAuto

View File

@@ -6,11 +6,9 @@
components:
- type: HealOnBuckle
damage:
groups:
Brute: -0.2 ## 0.1 per
Burn: -0.2
types:
Poison: -0.1
Blunt: -0.1
- type: Physics
bodyType: Static
- type: Fixtures
@@ -70,11 +68,10 @@
state: bed-MED
- type: HealOnBuckle
damage:
groups:
Brute: -0.4 ## 0.2 per
Burn: -0.4
types:
Poison: -0.2
Cold: -0.4
Blunt: -0.2
- type: Construction
graph: bed
node: medicalbed

View File

@@ -11,7 +11,7 @@
"name": "icon"
},
{
"name": "equipped-OUTERCLOTHING",
"name": "body-overlay-2",
"directions": 4
},
{

View File

@@ -108,3 +108,6 @@ Intercom: IntercomCommon
# 2023-07-12
ToyAssistant: ToyFigurinePassenger
# 2023-07-20
ForensicGloves: ClothingHandsGlovesForensic

View File

@@ -72,7 +72,6 @@ SERVER_CONTENT_ASSEMBLIES = [
SERVER_EXTRA_ASSEMBLIES = [
"Npgsql.",
"Microsoft",
"System.Linq.Async",
]
SERVER_NOT_EXTRA_ASSEMBLIES = [