diff --git a/Content.Client/GameObjects/Components/HandheldLightComponent.cs b/Content.Client/GameObjects/Components/HandheldLightComponent.cs index 1c09818ae0..30c4edd40a 100644 --- a/Content.Client/GameObjects/Components/HandheldLightComponent.cs +++ b/Content.Client/GameObjects/Components/HandheldLightComponent.cs @@ -14,10 +14,9 @@ namespace Content.Client.GameObjects.Components [RegisterComponent] public sealed class HandheldLightComponent : SharedHandheldLightComponent, IItemStatus { - private bool _hasCell; + [ViewVariables] protected override bool HasCell => _level != null; - [ViewVariables] public float? Charge { get; private set; } - [ViewVariables] protected override bool HasCell => _hasCell; + private byte? _level; public Control MakeControl() { @@ -29,8 +28,7 @@ namespace Content.Client.GameObjects.Components if (!(curState is HandheldLightComponentState cast)) return; - Charge = cast.Charge; - _hasCell = cast.HasCell; + _level = cast.Charge; } private sealed class StatusControl : Control @@ -38,7 +36,7 @@ namespace Content.Client.GameObjects.Components private const float TimerCycle = 1; private readonly HandheldLightComponent _parent; - private readonly PanelContainer[] _sections = new PanelContainer[5]; + private readonly PanelContainer[] _sections = new PanelContainer[StatusLevels - 1]; private float _timer; @@ -76,40 +74,37 @@ namespace Content.Client.GameObjects.Components { base.Update(args); + if (!_parent.HasCell) + return; + _timer += args.DeltaSeconds; _timer %= TimerCycle; - var charge = _parent.Charge ?? 0; + var level = _parent._level; - int level; + for (var i = 0; i < _sections.Length; i++) + { + if (i == 0) + { + if (level == 0) + { + _sections[0].PanelOverride = _styleBoxUnlit; + } + else if (level == 1) + { + // Flash the last light. + _sections[0].PanelOverride = _timer > TimerCycle / 2 ? _styleBoxLit : _styleBoxUnlit; + } + else + { + _sections[0].PanelOverride = _styleBoxLit; + } - if (MathHelper.CloseTo(charge, 0)) - { - level = 0; - } - else - { - level = ContentHelpers.RoundToNearestLevels(charge, 1.0, 6) + 1; - } + continue; + } - if (level == 0) - { - _sections[0].PanelOverride = _styleBoxUnlit; + _sections[i].PanelOverride = level >= i + 2 ? _styleBoxLit : _styleBoxUnlit; } - else if (level == 1) - { - // Flash the last light. - _sections[0].PanelOverride = _timer > TimerCycle / 2 ? _styleBoxLit : _styleBoxUnlit; - } - else - { - _sections[0].PanelOverride = _styleBoxLit; - } - - _sections[1].PanelOverride = level >= 3 ? _styleBoxLit : _styleBoxUnlit; - _sections[2].PanelOverride = level >= 4 ? _styleBoxLit : _styleBoxUnlit; - _sections[3].PanelOverride = level >= 5 ? _styleBoxLit : _styleBoxUnlit; - _sections[4].PanelOverride = level >= 6 ? _styleBoxLit : _styleBoxUnlit; } } } diff --git a/Content.Client/GameObjects/Components/IconSmoothing/IconSmoothComponent.cs b/Content.Client/GameObjects/Components/IconSmoothing/IconSmoothComponent.cs index 2513b6f062..f1e432e50c 100644 --- a/Content.Client/GameObjects/Components/IconSmoothing/IconSmoothComponent.cs +++ b/Content.Client/GameObjects/Components/IconSmoothing/IconSmoothComponent.cs @@ -203,7 +203,17 @@ namespace Content.Client.GameObjects.Components.IconSmoothing cornerNW |= CornerFill.Diagonal; } - return (cornerNE, cornerNW, cornerSW, cornerSE); + switch (Owner.Transform.WorldRotation.GetCardinalDir()) + { + case Direction.North: + return (cornerSW, cornerSE, cornerNE, cornerNW); + case Direction.West: + return (cornerSE, cornerNE, cornerNW, cornerSW); + case Direction.South: + return (cornerNE, cornerNW, cornerSW, cornerSE); + default: + return (cornerNW, cornerSW, cornerSE, cornerNE); + } } /// diff --git a/Content.Client/GameObjects/Components/Power/PowerCellVisualizer.cs b/Content.Client/GameObjects/Components/Power/PowerCellVisualizer.cs index 408c09d7d6..0bd1ab8f2e 100644 --- a/Content.Client/GameObjects/Components/Power/PowerCellVisualizer.cs +++ b/Content.Client/GameObjects/Components/Power/PowerCellVisualizer.cs @@ -34,10 +34,10 @@ namespace Content.Client.GameObjects.Components.Power base.OnChangeData(component); var sprite = component.Owner.GetComponent(); - if (component.TryGetData(PowerCellVisuals.ChargeLevel, out float fraction)) + if (component.TryGetData(PowerCellVisuals.ChargeLevel, out byte level)) { - int level = ContentHelpers.RoundToNearestLevels(fraction, 1, 4) * 25; - sprite.LayerSetState(Layers.Charge, $"{_prefix}_{level}"); + var adjustedLevel = level * 25; + sprite.LayerSetState(Layers.Charge, $"{_prefix}_{adjustedLevel}"); } } diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs index c906233b6c..b54be45138 100644 --- a/Content.Client/IgnoredComponents.cs +++ b/Content.Client/IgnoredComponents.cs @@ -212,6 +212,8 @@ "MorgueEntityStorage", "MorgueTray", "CrematoriumEntityStorage", + "RandomArcade", + "RandomSpriteState", }; } } diff --git a/Content.Server/Construction/Completions/BuildComputer.cs b/Content.Server/Construction/Completions/BuildComputer.cs index a377e48993..b2eb368369 100644 --- a/Content.Server/Construction/Completions/BuildComputer.cs +++ b/Content.Server/Construction/Completions/BuildComputer.cs @@ -1,4 +1,5 @@ #nullable enable +using System.Linq; using System.Threading.Tasks; using Content.Server.GameObjects.Components.Construction; using Content.Shared.Construction; @@ -53,7 +54,18 @@ namespace Content.Server.Construction.Completions var computer = entityManager.SpawnEntity(boardComponent.Prototype, entity.Transform.Coordinates); computer.Transform.LocalRotation = entity.Transform.LocalRotation; - var computerContainer = ContainerManagerComponent.Ensure(Container, computer); + var computerContainer = ContainerManagerComponent.Ensure(Container, computer, out var existed); + + if (existed) + { + // In case there are any entities inside this, delete them. + foreach (var ent in computerContainer.ContainedEntities.ToArray()) + { + computerContainer.ForceRemove(ent); + ent.Delete(); + } + } + computerContainer.Insert(board); if (computer.TryGetComponent(out ConstructionComponent? construction)) diff --git a/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs b/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs index ea1492f14a..3774218410 100644 --- a/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs +++ b/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs @@ -7,12 +7,14 @@ using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Localization; +using Robust.Shared.Maths; using Robust.Shared.Serialization; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; @@ -41,6 +43,11 @@ namespace Content.Server.GameObjects.Components.Interactable [ViewVariables(VVAccess.ReadWrite)] public string? TurnOnFailSound; [ViewVariables(VVAccess.ReadWrite)] public string? TurnOffSound; + /// + /// Client-side ItemStatus level + /// + private byte? _lastLevel; + public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); @@ -190,24 +197,34 @@ namespace Content.Server.GameObjects.Components.Interactable } if (Activated && !Cell.TryUseCharge(Wattage * frameTime)) TurnOff(false); - Dirty(); + + var level = GetLevel(); + + if (level != _lastLevel) + { + _lastLevel = level; + Dirty(); + } + } + + // Curently every single flashlight has the same number of levels for status and that's all it uses the charge for + // Thus we'll just check if the level changes. + private byte? GetLevel() + { + if (Cell == null) + return null; + + var currentCharge = Cell.CurrentCharge; + + if (MathHelper.CloseTo(currentCharge, 0) || Wattage > currentCharge) + return 0; + + return (byte?) ContentHelpers.RoundToNearestLevels(currentCharge / Cell.MaxCharge * 255, 255, StatusLevels); } public override ComponentState GetComponentState() { - if (Cell == null) - { - return new HandheldLightComponentState(null, false); - } - - if (Wattage > Cell.CurrentCharge) - { - // Practically zero. - // This is so the item status works correctly. - return new HandheldLightComponentState(0, HasCell); - } - - return new HandheldLightComponentState(Cell.CurrentCharge / Cell.MaxCharge, HasCell); + return new HandheldLightComponentState(GetLevel()); } } } diff --git a/Content.Server/GameObjects/Components/MachineLinking/ISignalReceiver.cs b/Content.Server/GameObjects/Components/MachineLinking/ISignalReceiver.cs index 78e032606f..412e0570d3 100644 --- a/Content.Server/GameObjects/Components/MachineLinking/ISignalReceiver.cs +++ b/Content.Server/GameObjects/Components/MachineLinking/ISignalReceiver.cs @@ -1,14 +1,9 @@ -namespace Content.Server.GameObjects.Components.MachineLinking -{ - public interface ISignalReceiver - { - void TriggerSignal(SignalState state); - } +using System; - public enum SignalState +namespace Content.Server.GameObjects.Components.MachineLinking +{ + public interface ISignalReceiver { - On, - Off, - Toggle + void TriggerSignal(T signal); } } diff --git a/Content.Server/GameObjects/Components/MachineLinking/SignalButtonComponent.cs b/Content.Server/GameObjects/Components/MachineLinking/SignalButtonComponent.cs index 4fc80c5902..928f774281 100644 --- a/Content.Server/GameObjects/Components/MachineLinking/SignalButtonComponent.cs +++ b/Content.Server/GameObjects/Components/MachineLinking/SignalButtonComponent.cs @@ -1,4 +1,5 @@ -using Content.Shared.Interfaces; +using Content.Server.GameObjects.Components.MachineLinking.Signals; +using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -29,7 +30,7 @@ namespace Content.Server.GameObjects.Components.MachineLinking return; } - if (transmitter.TransmitSignal(user, SignalState.Toggle)) + if (transmitter.TransmitSignal(user, new ToggleSignal())) { // Since the button doesn't have an animation, I'm going to use a popup message Owner.PopupMessage(user, Loc.GetString("Click.")); diff --git a/Content.Server/GameObjects/Components/MachineLinking/SignalReceiverComponent.cs b/Content.Server/GameObjects/Components/MachineLinking/SignalReceiverComponent.cs index d72e9ca5ec..0ae94e641c 100644 --- a/Content.Server/GameObjects/Components/MachineLinking/SignalReceiverComponent.cs +++ b/Content.Server/GameObjects/Components/MachineLinking/SignalReceiverComponent.cs @@ -26,9 +26,9 @@ namespace Content.Server.GameObjects.Components.MachineLinking _transmitters = new List(); } - public void DistributeSignal(SignalState state) + public void DistributeSignal(T state) { - foreach (var comp in Owner.GetAllComponents()) + foreach (var comp in Owner.GetAllComponents>()) { comp.TriggerSignal(state); } diff --git a/Content.Server/GameObjects/Components/MachineLinking/SignalSwitchComponent.cs b/Content.Server/GameObjects/Components/MachineLinking/SignalSwitchComponent.cs index 6ad3734f3c..26e694f4e9 100644 --- a/Content.Server/GameObjects/Components/MachineLinking/SignalSwitchComponent.cs +++ b/Content.Server/GameObjects/Components/MachineLinking/SignalSwitchComponent.cs @@ -52,7 +52,7 @@ namespace Content.Server.GameObjects.Components.MachineLinking return; } - transmitter.TransmitSignal(user, _on ? SignalState.On : SignalState.Off); + transmitter.TransmitSignal(user, _on); } private void UpdateSprite() diff --git a/Content.Server/GameObjects/Components/MachineLinking/SignalTransmitterComponent.cs b/Content.Server/GameObjects/Components/MachineLinking/SignalTransmitterComponent.cs index eccdd7a2f9..081494309f 100644 --- a/Content.Server/GameObjects/Components/MachineLinking/SignalTransmitterComponent.cs +++ b/Content.Server/GameObjects/Components/MachineLinking/SignalTransmitterComponent.cs @@ -87,7 +87,7 @@ namespace Content.Server.GameObjects.Components.MachineLinking } } - public bool TransmitSignal(IEntity user, SignalState state) + public bool TransmitSignal(IEntity user, T signal) { if (_receivers.Count == 0) { @@ -102,7 +102,7 @@ namespace Content.Server.GameObjects.Components.MachineLinking continue; } - receiver.DistributeSignal(state); + receiver.DistributeSignal(signal); } return true; } diff --git a/Content.Server/GameObjects/Components/MachineLinking/Signals/ToggleSignal.cs b/Content.Server/GameObjects/Components/MachineLinking/Signals/ToggleSignal.cs new file mode 100644 index 0000000000..4d9e835bef --- /dev/null +++ b/Content.Server/GameObjects/Components/MachineLinking/Signals/ToggleSignal.cs @@ -0,0 +1,4 @@ +namespace Content.Server.GameObjects.Components.MachineLinking.Signals +{ + public struct ToggleSignal {} +} diff --git a/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs b/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs index 679d1096d2..366756931a 100644 --- a/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs +++ b/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.MachineLinking; +using Content.Server.GameObjects.Components.MachineLinking.Signals; using Content.Server.GameObjects.Components.Mobs; using Content.Shared.Damage; using Content.Shared.GameObjects.Components.Damage; @@ -28,7 +29,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece /// Component that represents a wall light. It has a light bulb that can be replaced when broken. /// [RegisterComponent] - public class PoweredLightComponent : Component, IInteractHand, IInteractUsing, IMapInit, ISignalReceiver + public class PoweredLightComponent : Component, IInteractHand, IInteractUsing, IMapInit, ISignalReceiver, ISignalReceiver { [Dependency] private IGameTiming _gameTiming = default!; @@ -62,23 +63,6 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece return InsertBulb(eventArgs.Using); } - public void TriggerSignal(SignalState state) - { - switch (state) - { - case SignalState.On: - _on = true; - break; - case SignalState.Off: - _on = false; - break; - case SignalState.Toggle: - _on = !_on; - break; - } - UpdateLight(); - } - public bool InteractHand(InteractHandEventArgs eventArgs) { if (!eventArgs.User.TryGetComponent(out IDamageableComponent damageableComponent)) @@ -252,5 +236,17 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece var entity = Owner.EntityManager.SpawnEntity(prototype, Owner.Transform.Coordinates); _lightBulbContainer.Insert(entity); } + + public void TriggerSignal(bool signal) + { + _on = signal; + UpdateLight(); + } + + public void TriggerSignal(ToggleSignal signal) + { + _on = !_on; + UpdateLight(); + } } } diff --git a/Content.Server/GameObjects/Components/Power/PowerCellComponent.cs b/Content.Server/GameObjects/Components/Power/PowerCellComponent.cs index 9241536250..8a16a7c7da 100644 --- a/Content.Server/GameObjects/Components/Power/PowerCellComponent.cs +++ b/Content.Server/GameObjects/Components/Power/PowerCellComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.GameObjects.Components.Power; using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Utility; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Localization; @@ -45,10 +46,15 @@ namespace Content.Server.GameObjects.Components.Power { if (Owner.TryGetComponent(out AppearanceComponent appearance)) { - appearance.SetData(PowerCellVisuals.ChargeLevel, CurrentCharge / MaxCharge); + appearance.SetData(PowerCellVisuals.ChargeLevel, GetLevel(CurrentCharge / MaxCharge)); } } + private byte GetLevel(float fraction) + { + return (byte) ContentHelpers.RoundToNearestLevels(fraction, 1, SharedPowerCell.PowerCellVisualsLevels); + } + void IExamine.Examine(FormattedMessage message, bool inDetailsRange) { if(inDetailsRange) diff --git a/Content.Shared/GameObjects/Components/Power/SharedPowerCell.cs b/Content.Shared/GameObjects/Components/Power/SharedPowerCell.cs index 22b932883a..d460a8dd3c 100644 --- a/Content.Shared/GameObjects/Components/Power/SharedPowerCell.cs +++ b/Content.Shared/GameObjects/Components/Power/SharedPowerCell.cs @@ -3,6 +3,11 @@ using Robust.Shared.Serialization; namespace Content.Shared.GameObjects.Components.Power { + public static class SharedPowerCell + { + public const int PowerCellVisualsLevels = 4; + } + [Serializable, NetSerializable] public enum PowerCellVisuals { diff --git a/Content.Shared/GameObjects/Components/SharedHandheldLightComponent.cs b/Content.Shared/GameObjects/Components/SharedHandheldLightComponent.cs index f6dd37fcef..085d6ca06c 100644 --- a/Content.Shared/GameObjects/Components/SharedHandheldLightComponent.cs +++ b/Content.Shared/GameObjects/Components/SharedHandheldLightComponent.cs @@ -11,18 +11,17 @@ namespace Content.Shared.GameObjects.Components protected abstract bool HasCell { get; } + protected const int StatusLevels = 6; + [Serializable, NetSerializable] protected sealed class HandheldLightComponentState : ComponentState { - public HandheldLightComponentState(float? charge, bool hasCell) : base(ContentNetIDs.HANDHELD_LIGHT) + public byte? Charge { get; } + + public HandheldLightComponentState(byte? charge) : base(ContentNetIDs.HANDHELD_LIGHT) { Charge = charge; - HasCell = hasCell; } - - public float? Charge { get; } - - public bool HasCell { get; } } } diff --git a/RobustToolbox b/RobustToolbox index 2f21a2551e..3c428a17a8 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 2f21a2551e28a4cff2025e801cd87be08c0d6a97 +Subproject commit 3c428a17a87709b9527c930f97e327c1c0e29940