Resolves ApcVisualizer is Obsolete (#13898)

This commit is contained in:
TemporalOroboros
2023-05-07 19:17:29 -07:00
committed by GitHub
parent 3581db962c
commit 0658acd7cb
30 changed files with 490 additions and 185 deletions

View File

@@ -1,45 +0,0 @@
using Content.Shared.APC;
using Robust.Client.GameObjects;
namespace Content.Client.Power.APC
{
public sealed class ApcVisualizer : AppearanceVisualizer
{
public static readonly Color LackColor = Color.FromHex("#d1332e");
public static readonly Color ChargingColor = Color.FromHex("#2e8ad1");
public static readonly Color FullColor = Color.FromHex("#3db83b");
public static readonly Color EmagColor = Color.FromHex("#1f48d6");
[Obsolete("Subscribe to AppearanceChangeEvent instead.")]
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var ent = IoCManager.Resolve<IEntityManager>();
var sprite = ent.GetComponent<SpriteComponent>(component.Owner);
if (component.TryGetData<ApcChargeState>(ApcVisuals.ChargeState, out var chargeState))
{
if (ent.TryGetComponent(component.Owner, out SharedPointLightComponent? light))
{
light.Color = chargeState switch
{
ApcChargeState.Lack => LackColor,
ApcChargeState.Charging => ChargingColor,
ApcChargeState.Full => FullColor,
ApcChargeState.Emag => EmagColor,
_ => LackColor
};
}
}
}
enum ApcVisualLayers : byte
{
ChargeState,
Lock,
Equipment,
Lighting,
Environment,
}
}
}

View File

@@ -0,0 +1,107 @@
using Content.Shared.APC;
namespace Content.Client.Power.APC;
[RegisterComponent]
[Access(typeof(ApcVisualizerSystem))]
public sealed class ApcVisualsComponent : Component
{
#region Indicators
#region Locks
/// <summary>
/// The number of lock indicators on the APC.
/// </summary>
[DataField("numLockIndicators")]
[ViewVariables(VVAccess.ReadWrite)]
public byte LockIndicators = 2;
/// <summary>
/// The prefix used for the sprite state suffix of the lock indicator lights.
/// Valid states are of the form \<BASE\>\<PREFIX\>\<IDX>\-\<STATE\>
/// </summary>
[DataField("lockIndicatorPrefix")]
[ViewVariables(VVAccess.ReadWrite)]
public string LockPrefix = "lock";
/// <summary>
/// The suffixes used for the sprite state suffix of the lock indicator lights.
/// Valid states are of the form \<PREFIX\>\<IDX\>-\<STATE\>
/// </summary>
[DataField("lockIndicatorSuffixes")]
[ViewVariables(VVAccess.ReadWrite)]
public string[] LockSuffixes = new string[(byte)(2 << (sbyte)ApcLockState.LogWidth)]{"unlocked", "locked"};
#endregion Locks
#region Channels
/// <summary>
/// The number of output channel indicator lights on the APC.
/// </summary>
[DataField("numChannelIndicators")]
[ViewVariables(VVAccess.ReadWrite)]
public byte ChannelIndicators = 3;
/// <summary>
/// The prefix used for the sprite state suffix of the channel indicator lights.
/// Valid states are of the form \<BASE\>\<PREFIX\>\<IDX\>-\<STATE\>
/// </summary>
[DataField("channelIndicatorPrefix")]
[ViewVariables(VVAccess.ReadWrite)]
public string ChannelPrefix = "channel";
/// <summary>
/// The suffixes used for the sprite state suffix of the channel indicator lights.
/// Valid states are of the form \<PREFIX\>\<IDX\>-\<STATE\>
/// </summary>
[DataField("channelIndicatorSuffixes")]
[ViewVariables(VVAccess.ReadWrite)]
public string[] ChannelSuffixes = new string[(byte)(2 << (sbyte)ApcChannelState.LogWidth)]{"auto_off", "manual_off", "auto_on", "manual_on"};
#endregion Channels
#endregion Indicators
#region Screen
/// <summary>
/// The prefix used to construct the sprite state suffix used for the screen overlay.
/// Valid sprite states are of the form \<PREFIX\>-\<SUFFIX\>.
/// </summary>
[DataField("screenStatePrefix")]
[ViewVariables(VVAccess.ReadWrite)]
public string ScreenPrefix = "display";
/// <summary>
/// The suffix used to construct the sprite state suffix used for the screen overlay.
/// Valid sprite states are of the form \<PREFIX\>-\<STATE\>.
/// </summary>
[DataField("screenStateSuffixes")]
[ViewVariables(VVAccess.ReadWrite)]
public string[] ScreenSuffixes = new string[(byte)ApcChargeState.NumStates]{"lack", "charging", "full", "remote"};
/// <summary>
/// The colors of the light emitted by the APC given a particular display state.
/// </summary>
[DataField("screenColors")]
[ViewVariables(VVAccess.ReadWrite)]
public Color[] ScreenColors = new Color[(byte)ApcChargeState.NumStates]{Color.FromHex("#d1332e"), Color.FromHex("#2e8ad1"), Color.FromHex("#3db83b"), Color.FromHex("#ffac1c")};
/// <summary>
/// The sprite state of the unlit overlay used for the APC screen when the APC has been emagged.
/// </summary>
[DataField("emaggedScreenState")]
[ViewVariables(VVAccess.ReadWrite)]
public string EmaggedScreenState = "emag-unlit";
/// <summary>
/// The sprite state of the unlit overlay used for the APC screen when the APC has been emagged.
/// </summary>
[DataField("emaggedScreenColor")]
[ViewVariables(VVAccess.ReadWrite)]
public Color EmaggedScreenColor = Color.FromHex("#1f48d6");
#endregion Screen
}

View File

@@ -0,0 +1,106 @@
using Content.Shared.APC;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.Power.APC;
public sealed class ApcVisualizerSystem : VisualizerSystem<ApcVisualsComponent>
{
protected override void OnAppearanceChange(EntityUid uid, ApcVisualsComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
// Handle APC screen overlay:
if(!AppearanceSystem.TryGetData<ApcChargeState>(uid, ApcVisuals.ChargeState, out var chargeState, args.Component))
chargeState = ApcChargeState.Lack;
if (chargeState >= 0 && chargeState < ApcChargeState.NumStates)
{
args.Sprite.LayerSetState(ApcVisualLayers.ChargeState, $"{comp.ScreenPrefix}-{comp.ScreenSuffixes[(sbyte)chargeState]}");
// LockState does nothing currently. The backend doesn't exist.
if (AppearanceSystem.TryGetData<byte>(uid, ApcVisuals.LockState, out var lockStates, args.Component))
{
for(var i = 0; i < comp.LockIndicators; ++i)
{
var layer = ((byte)ApcVisualLayers.LockIndicatorOverlayStart + i);
sbyte lockState = (sbyte)((lockStates >> (i << (sbyte)ApcLockState.LogWidth)) & (sbyte)ApcLockState.All);
args.Sprite.LayerSetState(layer, $"{comp.LockPrefix}{i}-{comp.LockSuffixes[lockState]}");
args.Sprite.LayerSetVisible(layer, true);
}
}
// ChannelState does nothing currently. The backend doesn't exist.
if (AppearanceSystem.TryGetData<byte>(uid, ApcVisuals.ChannelState, out var channelStates, args.Component))
{
for(var i = 0; i < comp.ChannelIndicators; ++i)
{
var layer = ((byte)ApcVisualLayers.ChannelIndicatorOverlayStart + i);
sbyte channelState = (sbyte)((channelStates >> (i << (sbyte)ApcChannelState.LogWidth)) & (sbyte)ApcChannelState.All);
args.Sprite.LayerSetState(layer, $"{comp.ChannelPrefix}{i}-{comp.ChannelSuffixes[channelState]}");
args.Sprite.LayerSetVisible(layer, true);
}
}
if (TryComp<SharedPointLightComponent>(uid, out var light))
light.Color = comp.ScreenColors[(sbyte)chargeState];
}
else
{
/// Overrides all of the lock and channel indicators.
args.Sprite.LayerSetState(ApcVisualLayers.ChargeState, comp.EmaggedScreenState);
for(var i = 0; i < comp.LockIndicators; ++i)
{
var layer = ((byte)ApcVisualLayers.LockIndicatorOverlayStart + i);
args.Sprite.LayerSetVisible(layer, false);
}
for(var i = 0; i < comp.ChannelIndicators; ++i)
{
var layer = ((byte)ApcVisualLayers.ChannelIndicatorOverlayStart + i);
args.Sprite.LayerSetVisible(layer, false);
}
if (TryComp<SharedPointLightComponent>(uid, out var light))
light.Color = comp.EmaggedScreenColor;
}
}
}
enum ApcVisualLayers : byte
{
/// <summary>
/// The sprite layer used for the interface lock indicator light overlay.
/// </summary>
InterfaceLock = 0,
/// <summary>
/// The sprite layer used for the panel lock indicator light overlay.
/// </summary>
PanelLock = 1,
/// <summary>
/// The first of the lock indicator light layers.
/// </summary>
LockIndicatorOverlayStart = InterfaceLock,
/// <summary>
/// The sprite layer used for the equipment channel indicator light overlay.
/// </summary>
Equipment = 2,
/// <summary>
/// The sprite layer used for the lighting channel indicator light overlay.
/// </summary>
Lighting = 3,
/// <summary>
/// The sprite layer used for the environment channel indicator light overlay.
/// </summary>
Environment = 4,
/// <summary>
/// The first of the channel status indicator light layers.
/// </summary>
ChannelIndicatorOverlayStart = Equipment,
/// <summary>
/// The sprite layer used for the APC screen overlay.
/// </summary>
ChargeState = 5,
}

View File

@@ -3,36 +3,175 @@ using Robust.Shared.Serialization;
namespace Content.Shared.APC
{
[Serializable, NetSerializable]
public enum ApcVisuals
public enum ApcVisuals : byte
{
/// <summary>
/// APC lights/HUD.
/// APC locks.
/// </summary>
LockState,
/// <summary>
/// APC channels.
/// </summary>
ChannelState,
/// <summary>
/// APC lights/HUD.
/// </summary>
ChargeState,
}
[Serializable, NetSerializable]
public enum ApcChargeState
public enum ApcPanelState : sbyte
{
/// <summary>
/// APC does not have enough power to charge cell (if necessary) and keep powering the area.
/// APC is closed.
/// </summary>
Lack,
Closed = 0,
/// <summary>
/// APC is opened.
/// </summary>
Open = 1,
/// <summary>
/// APC is oaisdoj.
/// </summary>
Error = -1,
}
/// <summary>
/// The state of the APC interface lock.
/// None of this is implemented.
/// </summary>
[Serializable, NetSerializable]
public enum ApcLockState : sbyte
{
/// <summary>
/// Empty bitmask.
/// </summary>
None = 0,
/// <summary>
/// APC is not full but has enough power.
/// Bitfield indicating status of APC lock indicator.
/// </summary>
Charging,
Lock = (1<<0),
/// <summary>
/// Bit state indicating that the given APC lock is unlocked.
/// </summary>
Unlocked = None,
/// <summary>
/// Bit state indicating that the given APC lock is locked.
/// </summary>
Locked = (1<<0),
/// <summary>
/// APC battery is full and has enough power.
/// Bitmask for the full state for a given APC lock indicator.
/// </summary>
Full,
All = (Lock),
/// <summary>
/// The log 2 width in bits of the bitfields indicating the status of an APC lock indicator.
/// Used for bit shifting operations (Mask for the state for indicator i is (All << (i << LogWidth))).
/// </summary>
LogWidth = 0,
}
/// <summary>
/// APC power channel states.
/// None of this is implemented.
/// </summary>
public enum ApcChannelState : sbyte
{
/// <summary>
/// Empty bitmask.
/// </summary>
None = 0,
/// <summary>
/// Bitfield indicating whether the APC is automatically regulating the given channel.
/// </summary>
Control = (1<<0),
/// <summary>
/// Bit state indicating that the APC has been set to automatically toggle the given channel depending on available power.
/// </summary>
Auto = None,
/// <summary>
/// Bit state indicating that the APC has been set to always provide/not provide power on the given channel if possible.
/// </summary>
Manual = Control,
/// <summary>
/// Bitfield indicating whether the APC is currently providing power on the given channel.
/// </summary>
Power = (1<<1),
/// <summary>
/// Bit state indicating that the APC is currently not providing power on the given channel.
/// </summary>
Off = None,
/// <summary>
/// Bit state indicating that the APC is currently providing power on the given channel.
/// </summary>
On = Power,
/// <summary>
/// Bitmask for the full state for a given APC power channel.
/// </summary>
All = Power | Control,
/// <summary>
/// State that indicates the given channel has been automatically disabled.
/// </summary>
AutoOff = (Off | Auto),
/// <summary>
/// State that indicates the given channel has been automatically enabled.
/// </summary>
AutoOn = (On | Auto),
/// <summary>
/// State that indicates the given channel has been manually disabled.
/// </summary>
ManualOff = (Off | Manual),
/// <summary>
/// State that indicates the given channel has been manually enabled.
/// </summary>
ManualOn = (On | Manual),
/// <summary>
/// The log 2 width in bits of the bitfields indicating the status of an APC power channel.
/// Used for bit shifting operations (Mask for the state for channel i is (All << (i << LogWidth))).
/// </summary>
LogWidth = 1,
}
[Serializable, NetSerializable]
public enum ApcChargeState : sbyte
{
/// <summary>
/// APC does not have enough power to charge cell (if necessary) and keep powering the area.
/// </summary>
Lack = 0,
/// <summary>
/// APC is not full but has enough power.
/// </summary>
Charging = 1,
/// <summary>
/// APC battery is full and has enough power.
/// </summary>
Full = 2,
/// <summary>
/// APC is being remotely accessed.
/// Currently unimplemented, though the corresponding sprite state exists in the RSI.
/// </summary>
Remote = 3,
/// <summary>
/// The number of valid states charge states the APC can be in.
/// </summary>
NumStates = 4,
/// <summary>
/// APC is emagged (and not displaying other useful power colors at a glance)
/// </summary>
Emag,
Emag = -1,
}
[Serializable, NetSerializable]

View File

@@ -114,9 +114,6 @@
- state: panel
- state: on
shader: unshaded
# - type: Appearance
# visuals:
# - type: ApcVisualizer
- type: NodeContainer
examinable: true
nodes:

View File

@@ -30,26 +30,29 @@
sprite: Structures/Power/apc.rsi
layers:
- state: base
- state: powered-1
shader: unshaded
map: ["enum.ApcVisualLayers.ChargeState"]
- state: apcox-0
shader: unshaded
map: ["enum.ApcVisualLayers.Lock"]
- state: apco0-3
shader: unshaded
map: ["enum.ApcVisualLayers.Equipment"]
- state: apco1-3
shader: unshaded
map: ["enum.ApcVisualLayers.Lighting"]
- state: apco2-3
shader: unshaded
map: ["enum.ApcVisualLayers.Environment"]
- state: panel
map: ["enum.WiresVisualLayers.MaintenancePanel"]
visible: false
- state: display-charging
shader: unshaded
map: ["enum.ApcVisualLayers.ChargeState"]
- state: lock0-unlocked
shader: unshaded
map: ["enum.ApcVisualLayers.InterfaceLock"]
- state: lock1-unlocked
shader: unshaded
map: ["enum.ApcVisualLayers.PanelLock"]
- state: channel0-auto_on
shader: unshaded
map: ["enum.ApcVisualLayers.Equipment"]
- state: channel1-auto_on
shader: unshaded
map: ["enum.ApcVisualLayers.Lighting"]
- state: channel2-auto_on
shader: unshaded
map: ["enum.ApcVisualLayers.Environment"]
- type: Appearance
visuals:
- type: ApcVisualizer
- type: ApcVisuals
- type: Battery
maxCharge: 50000
startingCharge: 0
@@ -91,14 +94,6 @@
BoardName: "APC"
LayoutId: APC
- type: WiresVisuals
- type: GenericVisualizer
visuals:
enum.ApcVisuals.ChargeState:
enum.ApcVisualLayers.ChargeState:
Lack: { state: powered-1 }
Charging: { state: powered-2 }
Full: { state: powered-3 }
Emag: { state: emag-unlit }
- type: Damageable
damageContainer: Inorganic
damageModifierSet: StrongMetallic
@@ -142,7 +137,7 @@
drawdepth: WallMountedItems
netsync: false
sprite: Structures/Power/apc.rsi
state: apcframe
state: frame
- type: Construction
graph: APC
node: apcFrame

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 B

View File

Before

Width:  |  Height:  |  Size: 154 B

After

Width:  |  Height:  |  Size: 154 B

View File

Before

Width:  |  Height:  |  Size: 156 B

After

Width:  |  Height:  |  Size: 156 B

View File

Before

Width:  |  Height:  |  Size: 157 B

After

Width:  |  Height:  |  Size: 157 B

View File

Before

Width:  |  Height:  |  Size: 158 B

After

Width:  |  Height:  |  Size: 158 B

View File

Before

Width:  |  Height:  |  Size: 154 B

After

Width:  |  Height:  |  Size: 154 B

View File

Before

Width:  |  Height:  |  Size: 156 B

After

Width:  |  Height:  |  Size: 156 B

View File

Before

Width:  |  Height:  |  Size: 157 B

After

Width:  |  Height:  |  Size: 157 B

View File

Before

Width:  |  Height:  |  Size: 158 B

After

Width:  |  Height:  |  Size: 158 B

View File

Before

Width:  |  Height:  |  Size: 154 B

After

Width:  |  Height:  |  Size: 154 B

View File

Before

Width:  |  Height:  |  Size: 156 B

After

Width:  |  Height:  |  Size: 156 B

View File

Before

Width:  |  Height:  |  Size: 157 B

After

Width:  |  Height:  |  Size: 157 B

View File

Before

Width:  |  Height:  |  Size: 158 B

After

Width:  |  Height:  |  Size: 158 B

View File

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 434 B

View File

Before

Width:  |  Height:  |  Size: 275 B

After

Width:  |  Height:  |  Size: 275 B

View File

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 282 B

View File

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 337 B

View File

Before

Width:  |  Height:  |  Size: 436 B

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

View File

@@ -7,11 +7,117 @@
"y": 32
},
"states": [
{
"name": "base"
},
{
"name": "broken"
},
{
"name": "apcframe"
"name": "frame"
},
{
"name": "panel"
},
{
"name": "lock0-unlocked"
},
{
"name": "lock0-locked"
},
{
"name": "lock1-unlocked"
},
{
"name": "lock1-locked"
},
{
"name": "channel0-auto_off"
},
{
"name": "channel0-manual_off"
},
{
"name": "channel0-auto_on"
},
{
"name": "channel0-manual_on"
},
{
"name": "channel1-auto_off"
},
{
"name": "channel1-manual_off"
},
{
"name": "channel1-auto_on"
},
{
"name": "channel1-manual_on"
},
{
"name": "channel2-auto_off"
},
{
"name": "channel2-manual_off"
},
{
"name": "channel2-auto_on"
},
{
"name": "channel2-manual_on"
},
{
"name": "display-lack",
"delays": [
[
1,
2
]
]
},
{
"name": "display-charging",
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.2
]
]
},
{
"name": "display-full",
"delays": [
[
1,
1
]
]
},
{
"name": "display-remote",
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1
]
]
},
{
"name": "emag-unlit",
"delays": [
[
0.5,
0.5
]
]
},
{
"name": "sparks-unlit",
@@ -25,106 +131,6 @@
0.1
]
]
},
{
"name": "base"
},
{
"name": "emag-unlit",
"delays": [
[
0.5,
0.5
]
]
},
{
"name": "panel"
},
{
"name": "apco0-0"
},
{
"name": "apco0-1"
},
{
"name": "apco0-2"
},
{
"name": "apco0-3"
},
{
"name": "apco1-0"
},
{
"name": "apco1-1"
},
{
"name": "apco1-2"
},
{
"name": "apco1-3"
},
{
"name": "apco2-0"
},
{
"name": "apco2-1"
},
{
"name": "apco2-2"
},
{
"name": "apco2-3"
},
{
"name": "powered-1",
"delays": [
[
1,
2
]
]
},
{
"name": "powered-2",
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.2
]
]
},
{
"name": "powered-3",
"delays": [
[
1,
1
]
]
},
{
"name": "apco3-3",
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1
]
]
},
{
"name": "apcox-0"
},
{
"name": "apcox-1"
}
]
}