Fix item/clothing visual & networking bugs (#10116)
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
using Content.Client.Items.Components;
|
using Content.Shared.Light;
|
||||||
using Content.Shared.Light.Component;
|
using Content.Shared.Light.Component;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
@@ -6,103 +6,79 @@ using Robust.Client.UserInterface.Controls;
|
|||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||||
|
|
||||||
namespace Content.Client.Light.Components
|
namespace Content.Client.Light.Components;
|
||||||
|
|
||||||
|
public sealed class HandheldLightStatus : Control
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
private const float TimerCycle = 1;
|
||||||
[Access(typeof(HandheldLightSystem))]
|
|
||||||
public sealed class HandheldLightComponent : SharedHandheldLightComponent, IItemStatus
|
private readonly HandheldLightComponent _parent;
|
||||||
|
private readonly PanelContainer[] _sections = new PanelContainer[HandheldLightComponent.StatusLevels - 1];
|
||||||
|
|
||||||
|
private float _timer;
|
||||||
|
|
||||||
|
private static readonly StyleBoxFlat StyleBoxLit = new()
|
||||||
{
|
{
|
||||||
public byte? Level;
|
BackgroundColor = Color.LimeGreen
|
||||||
public bool Activated;
|
};
|
||||||
|
|
||||||
/// <summary>
|
private static readonly StyleBoxFlat StyleBoxUnlit = new()
|
||||||
/// Whether to automatically set item-prefixes when toggling the flashlight.
|
{
|
||||||
/// </summary>
|
BackgroundColor = Color.Black
|
||||||
/// <remarks>
|
};
|
||||||
/// Flashlights should probably be using explicit unshaded sprite, in-hand and clothing layers, this is
|
|
||||||
/// mostly here for backwards compatibility.
|
|
||||||
/// </remarks>
|
|
||||||
[DataField("addPrefix")]
|
|
||||||
public bool AddPrefix = false;
|
|
||||||
|
|
||||||
public Control MakeControl()
|
public HandheldLightStatus(HandheldLightComponent parent)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
|
||||||
|
var wrapper = new BoxContainer
|
||||||
{
|
{
|
||||||
return new StatusControl(this);
|
Orientation = LayoutOrientation.Horizontal,
|
||||||
|
SeparationOverride = 4,
|
||||||
|
HorizontalAlignment = HAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
AddChild(wrapper);
|
||||||
|
|
||||||
|
for (var i = 0; i < _sections.Length; i++)
|
||||||
|
{
|
||||||
|
var panel = new PanelContainer {MinSize = (20, 20)};
|
||||||
|
wrapper.AddChild(panel);
|
||||||
|
_sections[i] = panel;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class StatusControl : Control
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
|
_timer += args.DeltaSeconds;
|
||||||
|
_timer %= TimerCycle;
|
||||||
|
|
||||||
|
var level = _parent.Level;
|
||||||
|
|
||||||
|
for (var i = 0; i < _sections.Length; i++)
|
||||||
{
|
{
|
||||||
private const float TimerCycle = 1;
|
if (i == 0)
|
||||||
|
|
||||||
private readonly HandheldLightComponent _parent;
|
|
||||||
private readonly PanelContainer[] _sections = new PanelContainer[StatusLevels - 1];
|
|
||||||
|
|
||||||
private float _timer;
|
|
||||||
|
|
||||||
private static readonly StyleBoxFlat StyleBoxLit = new()
|
|
||||||
{
|
{
|
||||||
BackgroundColor = Color.LimeGreen
|
if (level == 0 || level == null)
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly StyleBoxFlat StyleBoxUnlit = new()
|
|
||||||
{
|
|
||||||
BackgroundColor = Color.Black
|
|
||||||
};
|
|
||||||
|
|
||||||
public StatusControl(HandheldLightComponent parent)
|
|
||||||
{
|
|
||||||
_parent = parent;
|
|
||||||
|
|
||||||
var wrapper = new BoxContainer
|
|
||||||
{
|
{
|
||||||
Orientation = LayoutOrientation.Horizontal,
|
_sections[0].PanelOverride = StyleBoxUnlit;
|
||||||
SeparationOverride = 4,
|
|
||||||
HorizontalAlignment = HAlignment.Center
|
|
||||||
};
|
|
||||||
|
|
||||||
AddChild(wrapper);
|
|
||||||
|
|
||||||
for (var i = 0; i < _sections.Length; i++)
|
|
||||||
{
|
|
||||||
var panel = new PanelContainer {MinSize = (20, 20)};
|
|
||||||
wrapper.AddChild(panel);
|
|
||||||
_sections[i] = panel;
|
|
||||||
}
|
}
|
||||||
|
else if (level == 1)
|
||||||
|
{
|
||||||
|
// Flash the last light.
|
||||||
|
_sections[0].PanelOverride = _timer > TimerCycle / 2 ? StyleBoxLit : StyleBoxUnlit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_sections[0].PanelOverride = StyleBoxLit;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void FrameUpdate(FrameEventArgs args)
|
_sections[i].PanelOverride = level >= i + 2 ? StyleBoxLit : StyleBoxUnlit;
|
||||||
{
|
|
||||||
base.FrameUpdate(args);
|
|
||||||
|
|
||||||
_timer += args.DeltaSeconds;
|
|
||||||
_timer %= TimerCycle;
|
|
||||||
|
|
||||||
var level = _parent.Level;
|
|
||||||
|
|
||||||
for (var i = 0; i < _sections.Length; i++)
|
|
||||||
{
|
|
||||||
if (i == 0)
|
|
||||||
{
|
|
||||||
if (level == 0 || level == null)
|
|
||||||
{
|
|
||||||
_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_sections[i].PanelOverride = level >= i + 2 ? StyleBoxLit : StyleBoxUnlit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,21 @@
|
|||||||
using Content.Client.Items.Systems;
|
using Content.Client.Items;
|
||||||
using Content.Client.Light.Components;
|
using Content.Client.Light.Components;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Light;
|
||||||
using Content.Shared.Light.Component;
|
using Content.Shared.Light.Component;
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
|
|
||||||
namespace Content.Client.Light;
|
namespace Content.Client.Light;
|
||||||
|
|
||||||
public sealed class HandheldLightSystem : EntitySystem
|
public sealed class HandheldLightSystem : SharedHandheldLightSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly ItemSystem _itemSys = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<HandheldLightComponent, ComponentHandleState>(OnHandleState);
|
|
||||||
|
SubscribeLocalEvent<HandheldLightComponent, ItemStatusCollectMessage>(OnGetStatusControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHandleState(EntityUid uid, HandheldLightComponent component, ref ComponentHandleState args)
|
private static void OnGetStatusControl(EntityUid uid, HandheldLightComponent component, ItemStatusCollectMessage args)
|
||||||
{
|
{
|
||||||
if (args.Current is not SharedHandheldLightComponent.HandheldLightComponentState state)
|
args.Controls.Add(new HandheldLightStatus(component));
|
||||||
return;
|
|
||||||
|
|
||||||
component.Level = state.Charge;
|
|
||||||
|
|
||||||
if (state.Activated == component.Activated)
|
|
||||||
return;
|
|
||||||
|
|
||||||
component.Activated = state.Activated;
|
|
||||||
|
|
||||||
// really hand-held lights should be using a separate unshaded layer. (see FlashlightVisualizer)
|
|
||||||
// this prefix stuff is largely for backwards compatibility with RSIs/yamls that have not been updated.
|
|
||||||
if (component.AddPrefix && TryComp(uid, out ItemComponent? item))
|
|
||||||
{
|
|
||||||
_itemSys.SetHeldPrefix(uid, state.Activated ? "on" : "off", item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Content.Shared.Light;
|
||||||
using Content.Shared.Light.Component;
|
using Content.Shared.Light.Component;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.Animations;
|
using Robust.Client.Animations;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Clothing.Components;
|
using Content.Server.Clothing.Components;
|
||||||
using Content.Server.Inventory;
|
using Content.Server.Inventory;
|
||||||
|
using Content.Shared.Clothing.EntitySystems;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@@ -37,7 +38,8 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
var child = entMgr.SpawnEntity(null, MapCoordinates.Nullspace);
|
var child = entMgr.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||||
var item = entMgr.AddComponent<ClothingComponent>(child);
|
var item = entMgr.AddComponent<ClothingComponent>(child);
|
||||||
item.Slots = SlotFlags.HEAD;
|
|
||||||
|
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ClothingSystem>().SetSlots(item.Owner, SlotFlags.HEAD, item);
|
||||||
|
|
||||||
// Equip item.
|
// Equip item.
|
||||||
Assert.That(invSystem.TryEquip(container, child, "head"), Is.True);
|
Assert.That(invSystem.TryEquip(container, child, "head"), Is.True);
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
using Content.Server.Light.EntitySystems;
|
|
||||||
using Content.Shared.Light.Component;
|
|
||||||
using Content.Shared.Sound;
|
|
||||||
|
|
||||||
namespace Content.Server.Light.Components
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Component that represents a powered handheld light source which can be toggled on and off.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
[Access(typeof(HandheldLightSystem))]
|
|
||||||
public sealed class HandheldLightComponent : SharedHandheldLightComponent
|
|
||||||
{
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("wattage")] public float Wattage { get; set; } = .8f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Status of light, whether or not it is emitting light.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public bool Activated { get; set; }
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("turnOnSound")] public SoundSpecifier TurnOnSound = new SoundPathSpecifier("/Audio/Items/flashlight_on.ogg");
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("turnOnFailSound")] public SoundSpecifier TurnOnFailSound = new SoundPathSpecifier("/Audio/Machines/button.ogg");
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("turnOffSound")] public SoundSpecifier TurnOffSound = new SoundPathSpecifier("/Audio/Items/flashlight_off.ogg");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Client-side ItemStatus level
|
|
||||||
/// </summary>
|
|
||||||
public byte? LastLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
using Content.Server.Actions;
|
using Content.Server.Actions;
|
||||||
using Content.Server.Light.Components;
|
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.PowerCell;
|
using Content.Server.PowerCell;
|
||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.Actions.ActionTypes;
|
using Content.Shared.Actions.ActionTypes;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Light.Component;
|
using Content.Shared.Light;
|
||||||
using Content.Shared.Rounding;
|
using Content.Shared.Rounding;
|
||||||
using Content.Shared.Toggleable;
|
using Content.Shared.Toggleable;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
@@ -23,7 +21,7 @@ using Robust.Shared.Utility;
|
|||||||
namespace Content.Server.Light.EntitySystems
|
namespace Content.Server.Light.EntitySystems
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed class HandheldLightSystem : EntitySystem
|
public sealed class HandheldLightSystem : SharedHandheldLightSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||||
@@ -38,7 +36,6 @@ namespace Content.Server.Light.EntitySystems
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<HandheldLightComponent, ComponentInit>(OnInit);
|
|
||||||
SubscribeLocalEvent<HandheldLightComponent, ComponentRemove>(OnRemove);
|
SubscribeLocalEvent<HandheldLightComponent, ComponentRemove>(OnRemove);
|
||||||
SubscribeLocalEvent<HandheldLightComponent, ComponentGetState>(OnGetState);
|
SubscribeLocalEvent<HandheldLightComponent, ComponentGetState>(OnGetState);
|
||||||
|
|
||||||
@@ -99,7 +96,7 @@ namespace Content.Server.Light.EntitySystems
|
|||||||
|
|
||||||
private void OnGetState(EntityUid uid, HandheldLightComponent component, ref ComponentGetState args)
|
private void OnGetState(EntityUid uid, HandheldLightComponent component, ref ComponentGetState args)
|
||||||
{
|
{
|
||||||
args.State = new SharedHandheldLightComponent.HandheldLightComponentState(component.Activated, GetLevel(component));
|
args.State = new HandheldLightComponent.HandheldLightComponentState(component.Activated, GetLevel(component));
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte? GetLevel(HandheldLightComponent component)
|
private byte? GetLevel(HandheldLightComponent component)
|
||||||
@@ -113,15 +110,7 @@ namespace Content.Server.Light.EntitySystems
|
|||||||
if (MathHelper.CloseToPercent(battery.CurrentCharge, 0) || component.Wattage > battery.CurrentCharge)
|
if (MathHelper.CloseToPercent(battery.CurrentCharge, 0) || component.Wattage > battery.CurrentCharge)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return (byte?) ContentHelpers.RoundToNearestLevels(battery.CurrentCharge / battery.MaxCharge * 255, 255, SharedHandheldLightComponent.StatusLevels);
|
return (byte?) ContentHelpers.RoundToNearestLevels(battery.CurrentCharge / battery.MaxCharge * 255, 255, HandheldLightComponent.StatusLevels);
|
||||||
}
|
|
||||||
|
|
||||||
private void OnInit(EntityUid uid, HandheldLightComponent component, ComponentInit args)
|
|
||||||
{
|
|
||||||
EntityManager.EnsureComponent<PointLightComponent>(uid);
|
|
||||||
|
|
||||||
// Want to make sure client has latest data on level so battery displays properly.
|
|
||||||
component.Dirty(EntityManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRemove(EntityUid uid, HandheldLightComponent component, ComponentRemove args)
|
private void OnRemove(EntityUid uid, HandheldLightComponent component, ComponentRemove args)
|
||||||
@@ -201,19 +190,9 @@ namespace Content.Server.Light.EntitySystems
|
|||||||
{
|
{
|
||||||
if (!component.Activated) return false;
|
if (!component.Activated) return false;
|
||||||
|
|
||||||
component.Activated = false;
|
SetActivated(component.Owner, false, component, makeNoise);
|
||||||
if (component.ToggleAction != null)
|
component.Level = null;
|
||||||
_actionSystem.SetToggled(component.ToggleAction, false);
|
|
||||||
_activeLights.Remove(component);
|
_activeLights.Remove(component);
|
||||||
component.LastLevel = null;
|
|
||||||
Dirty(component);
|
|
||||||
|
|
||||||
if (TryComp(component.Owner, out AppearanceComponent? appearance))
|
|
||||||
appearance.SetData(ToggleableLightVisuals.Enabled, false);
|
|
||||||
|
|
||||||
if (makeNoise)
|
|
||||||
SoundSystem.Play(component.TurnOffSound.GetSound(), Filter.Pvs(component.Owner, entityManager: EntityManager), component.Owner);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,17 +218,9 @@ namespace Content.Server.Light.EntitySystems
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
component.Activated = true;
|
SetActivated(component.Owner, true, component, true);
|
||||||
if (component.ToggleAction != null)
|
|
||||||
_actionSystem.SetToggled(component.ToggleAction, true);
|
|
||||||
_activeLights.Add(component);
|
_activeLights.Add(component);
|
||||||
component.LastLevel = GetLevel(component);
|
|
||||||
Dirty(component);
|
|
||||||
|
|
||||||
if (TryComp(component.Owner, out AppearanceComponent? appearance))
|
|
||||||
appearance.SetData(ToggleableLightVisuals.Enabled, true);
|
|
||||||
|
|
||||||
SoundSystem.Play(component.TurnOnSound.GetSound(), Filter.Pvs(component.Owner, entityManager: EntityManager), component.Owner);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,10 +259,10 @@ namespace Content.Server.Light.EntitySystems
|
|||||||
{
|
{
|
||||||
var level = GetLevel(comp);
|
var level = GetLevel(comp);
|
||||||
|
|
||||||
if (level == comp.LastLevel)
|
if (level == comp.Level)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
comp.LastLevel = level;
|
comp.Level = level;
|
||||||
Dirty(comp);
|
Dirty(comp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using Content.Shared.Chemistry.Reagent;
|
|||||||
using Content.Shared.Clothing.EntitySystems;
|
using Content.Shared.Clothing.EntitySystems;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Item;
|
||||||
using Content.Shared.Smoking;
|
using Content.Shared.Smoking;
|
||||||
using Content.Shared.Temperature;
|
using Content.Shared.Temperature;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
@@ -26,6 +27,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||||
[Dependency] private readonly ClothingSystem _clothing = default!;
|
[Dependency] private readonly ClothingSystem _clothing = default!;
|
||||||
|
[Dependency] private readonly SharedItemSystem _items = default!;
|
||||||
|
|
||||||
private const float UpdateTimer = 3f;
|
private const float UpdateTimer = 3f;
|
||||||
|
|
||||||
@@ -61,6 +63,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
};
|
};
|
||||||
|
|
||||||
_clothing.SetEquippedPrefix(uid, newState, clothing);
|
_clothing.SetEquippedPrefix(uid, newState, clothing);
|
||||||
|
_items.SetHeldPrefix(uid, newState);
|
||||||
|
|
||||||
if (state == SmokableState.Lit)
|
if (state == SmokableState.Lit)
|
||||||
_active.Add(uid);
|
_active.Add(uid);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Clothing.EntitySystems;
|
using Content.Shared.Clothing.EntitySystems;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
@@ -10,9 +10,11 @@ namespace Content.Shared.Clothing.Components;
|
|||||||
/// This handles entities which can be equipped.
|
/// This handles entities which can be equipped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[NetworkedComponent]
|
[NetworkedComponent]
|
||||||
|
[Access(typeof(ClothingSystem), typeof(InventorySystem))]
|
||||||
public abstract class SharedClothingComponent : Component
|
public abstract class SharedClothingComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("clothingVisuals")]
|
[DataField("clothingVisuals")]
|
||||||
|
[Access(typeof(ClothingSystem), typeof(InventorySystem), Other = AccessPermissions.ReadExecute)] // TODO remove execute permissions.
|
||||||
public Dictionary<string, List<SharedSpriteComponent.PrototypeLayerData>> ClothingVisuals = new();
|
public Dictionary<string, List<SharedSpriteComponent.PrototypeLayerData>> ClothingVisuals = new();
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
@@ -21,6 +23,7 @@ public abstract class SharedClothingComponent : Component
|
|||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("slots", required: true)]
|
[DataField("slots", required: true)]
|
||||||
|
[Access(typeof(ClothingSystem), typeof(InventorySystem), Other = AccessPermissions.ReadExecute)]
|
||||||
public SlotFlags Slots = SlotFlags.NONE;
|
public SlotFlags Slots = SlotFlags.NONE;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
using Content.Shared.Clothing.Components;
|
using Content.Shared.Clothing.Components;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Item;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
namespace Content.Shared.Clothing.EntitySystems;
|
namespace Content.Shared.Clothing.EntitySystems;
|
||||||
|
|
||||||
public sealed class ClothingSystem : EntitySystem
|
public sealed class ClothingSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly SharedItemSystem _itemSys = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -21,17 +25,30 @@ public sealed class ClothingSystem : EntitySystem
|
|||||||
private void OnHandleState(EntityUid uid, SharedClothingComponent component, ref ComponentHandleState args)
|
private void OnHandleState(EntityUid uid, SharedClothingComponent component, ref ComponentHandleState args)
|
||||||
{
|
{
|
||||||
if (args.Current is ClothingComponentState state)
|
if (args.Current is ClothingComponentState state)
|
||||||
component.EquippedPrefix = state.EquippedPrefix;
|
SetEquippedPrefix(uid, state.EquippedPrefix, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Public API
|
#region Public API
|
||||||
|
|
||||||
public void SetEquippedPrefix(EntityUid uid, string? prefix, SharedClothingComponent? clothing = null)
|
public void SetEquippedPrefix(EntityUid uid, string? prefix, SharedClothingComponent? clothing = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref clothing))
|
if (!Resolve(uid, ref clothing, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (clothing.EquippedPrefix == prefix)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clothing.EquippedPrefix = prefix;
|
clothing.EquippedPrefix = prefix;
|
||||||
|
_itemSys.VisualsChanged(uid);
|
||||||
|
Dirty(clothing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSlots(EntityUid uid, SlotFlags slots, SharedClothingComponent? clothing = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref clothing))
|
||||||
|
return;
|
||||||
|
|
||||||
|
clothing.Slots = slots;
|
||||||
Dirty(clothing);
|
Dirty(clothing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Item;
|
namespace Content.Shared.Item;
|
||||||
@@ -8,11 +9,13 @@ namespace Content.Shared.Item;
|
|||||||
/// like backpacks.
|
/// like backpacks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
|
[NetworkedComponent]
|
||||||
|
[Access(typeof(SharedItemSystem))]
|
||||||
public sealed class ItemComponent : Component
|
public sealed class ItemComponent : Component
|
||||||
{
|
{
|
||||||
[Access(typeof(SharedItemSystem), Other = AccessPermissions.ReadExecute)]
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("size")]
|
[DataField("size")]
|
||||||
|
[Access(typeof(SharedItemSystem), Other = AccessPermissions.ReadExecute)]
|
||||||
public int Size = 5;
|
public int Size = 5;
|
||||||
|
|
||||||
[DataField("inhandVisuals")]
|
[DataField("inhandVisuals")]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
@@ -38,7 +38,10 @@ public abstract class SharedItemSystem : EntitySystem
|
|||||||
|
|
||||||
public void SetHeldPrefix(EntityUid uid, string? heldPrefix, ItemComponent? component = null)
|
public void SetHeldPrefix(EntityUid uid, string? heldPrefix, ItemComponent? component = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref component))
|
if (!Resolve(uid, ref component, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.HeldPrefix == heldPrefix)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
component.HeldPrefix = heldPrefix;
|
component.HeldPrefix = heldPrefix;
|
||||||
@@ -62,7 +65,7 @@ public abstract class SharedItemSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
component.Size = state.Size;
|
component.Size = state.Size;
|
||||||
component.HeldPrefix = state.HeldPrefix;
|
SetHeldPrefix(uid, state.HeldPrefix, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGetState(EntityUid uid, ItemComponent component, ref ComponentGetState args)
|
private void OnGetState(EntityUid uid, ItemComponent component, ref ComponentGetState args)
|
||||||
|
|||||||
76
Content.Shared/Light/Component/HandheldLightComponent.cs
Normal file
76
Content.Shared/Light/Component/HandheldLightComponent.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
using Content.Shared.Actions.ActionTypes;
|
||||||
|
using Content.Shared.Sound;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
|
namespace Content.Shared.Light
|
||||||
|
{
|
||||||
|
[NetworkedComponent]
|
||||||
|
[RegisterComponent]
|
||||||
|
[Access(typeof(SharedHandheldLightSystem))]
|
||||||
|
public sealed class HandheldLightComponent : Robust.Shared.GameObjects.Component
|
||||||
|
{
|
||||||
|
public byte? Level;
|
||||||
|
public bool Activated;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("wattage")]
|
||||||
|
public float Wattage { get; set; } = .8f;
|
||||||
|
|
||||||
|
[DataField("turnOnSound")]
|
||||||
|
public SoundSpecifier TurnOnSound = new SoundPathSpecifier("/Audio/Items/flashlight_on.ogg");
|
||||||
|
|
||||||
|
[DataField("turnOnFailSound")]
|
||||||
|
public SoundSpecifier TurnOnFailSound = new SoundPathSpecifier("/Audio/Machines/button.ogg");
|
||||||
|
|
||||||
|
[DataField("turnOffSound")]
|
||||||
|
public SoundSpecifier TurnOffSound = new SoundPathSpecifier("/Audio/Items/flashlight_off.ogg");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to automatically set item-prefixes when toggling the flashlight.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Flashlights should probably be using explicit unshaded sprite, in-hand and clothing layers, this is
|
||||||
|
/// mostly here for backwards compatibility.
|
||||||
|
/// </remarks>
|
||||||
|
[DataField("addPrefix")]
|
||||||
|
public bool AddPrefix = false;
|
||||||
|
|
||||||
|
[DataField("toggleActionId", customTypeSerializer: typeof(PrototypeIdSerializer<InstantActionPrototype>))]
|
||||||
|
public string ToggleActionId = "ToggleLight";
|
||||||
|
|
||||||
|
[DataField("toggleAction")]
|
||||||
|
public InstantAction? ToggleAction;
|
||||||
|
|
||||||
|
public const int StatusLevels = 6;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class HandheldLightComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public byte? Charge { get; }
|
||||||
|
|
||||||
|
public bool Activated { get; }
|
||||||
|
|
||||||
|
public HandheldLightComponentState(bool activated, byte? charge)
|
||||||
|
{
|
||||||
|
Activated = activated;
|
||||||
|
Charge = charge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum HandheldLightVisuals
|
||||||
|
{
|
||||||
|
Power
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum HandheldLightPowerStates
|
||||||
|
{
|
||||||
|
FullPower,
|
||||||
|
LowPower,
|
||||||
|
Dying,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
using Content.Shared.Actions.ActionTypes;
|
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
|
|
||||||
namespace Content.Shared.Light.Component
|
|
||||||
{
|
|
||||||
[NetworkedComponent]
|
|
||||||
public abstract class SharedHandheldLightComponent : Robust.Shared.GameObjects.Component
|
|
||||||
{
|
|
||||||
[DataField("toggleActionId", customTypeSerializer:typeof(PrototypeIdSerializer<InstantActionPrototype>))]
|
|
||||||
public string ToggleActionId = "ToggleLight";
|
|
||||||
|
|
||||||
[DataField("toggleAction")]
|
|
||||||
public InstantAction? ToggleAction;
|
|
||||||
|
|
||||||
public const int StatusLevels = 6;
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class HandheldLightComponentState : ComponentState
|
|
||||||
{
|
|
||||||
public byte? Charge { get; }
|
|
||||||
|
|
||||||
public bool Activated { get; }
|
|
||||||
|
|
||||||
public HandheldLightComponentState(bool activated, byte? charge)
|
|
||||||
{
|
|
||||||
Activated = activated;
|
|
||||||
Charge = charge;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum HandheldLightVisuals
|
|
||||||
{
|
|
||||||
Power
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum HandheldLightPowerStates
|
|
||||||
{
|
|
||||||
FullPower,
|
|
||||||
LowPower,
|
|
||||||
Dying,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
79
Content.Shared/Light/SharedHandheldLightSystem.cs
Normal file
79
Content.Shared/Light/SharedHandheldLightSystem.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Clothing.EntitySystems;
|
||||||
|
using Content.Shared.Item;
|
||||||
|
using Content.Shared.Toggleable;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.Light;
|
||||||
|
|
||||||
|
public abstract class SharedHandheldLightSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedItemSystem _itemSys = default!;
|
||||||
|
[Dependency] private readonly ClothingSystem _clothingSys = default!;
|
||||||
|
[Dependency] private readonly SharedActionsSystem _actionSystem = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<HandheldLightComponent, ComponentInit>(OnInit);
|
||||||
|
SubscribeLocalEvent<HandheldLightComponent, ComponentHandleState>(OnHandleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInit(EntityUid uid, HandheldLightComponent component, ComponentInit args)
|
||||||
|
{
|
||||||
|
UpdateVisuals(uid, component);
|
||||||
|
|
||||||
|
// Want to make sure client has latest data on level so battery displays properly.
|
||||||
|
Dirty(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHandleState(EntityUid uid, HandheldLightComponent component, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not HandheldLightComponent.HandheldLightComponentState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.Level = state.Charge;
|
||||||
|
SetActivated(uid, state.Activated, component, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetActivated(EntityUid uid, bool activated, HandheldLightComponent? component = null, bool makeNoise = true)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.Activated == activated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.Activated = activated;
|
||||||
|
|
||||||
|
if (makeNoise)
|
||||||
|
{
|
||||||
|
var sound = component.Activated ? component.TurnOnSound : component.TurnOffSound;
|
||||||
|
SoundSystem.Play(sound.GetSound(), Filter.Pvs(component.Owner, entityManager: EntityManager), component.Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dirty(component);
|
||||||
|
UpdateVisuals(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateVisuals(EntityUid uid, HandheldLightComponent? component = null, AppearanceComponent? appearance = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component, ref appearance, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.AddPrefix)
|
||||||
|
{
|
||||||
|
var prefix = component.Activated ? "on" : "off";
|
||||||
|
_itemSys.SetHeldPrefix(uid, prefix);
|
||||||
|
_clothingSys.SetEquippedPrefix(uid, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.ToggleAction != null)
|
||||||
|
_actionSystem.SetToggled(component.ToggleAction, component.Activated);
|
||||||
|
|
||||||
|
appearance.SetData(ToggleableLightVisuals.Enabled, component.Activated);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -292,6 +292,7 @@
|
|||||||
map: [ "light" ]
|
map: [ "light" ]
|
||||||
- type: HandheldLight
|
- type: HandheldLight
|
||||||
addPrefix: false
|
addPrefix: false
|
||||||
|
- type: ToggleableLightVisuals
|
||||||
clothingVisuals:
|
clothingVisuals:
|
||||||
head:
|
head:
|
||||||
- state: equipped-head-light
|
- state: equipped-head-light
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
startingItem: PowerCellHigh
|
startingItem: PowerCellHigh
|
||||||
- type: HandheldLight
|
- type: HandheldLight
|
||||||
addPrefix: false
|
addPrefix: false
|
||||||
|
- type: ToggleableLightVisuals
|
||||||
inhandVisuals:
|
inhandVisuals:
|
||||||
left:
|
left:
|
||||||
- state: inhand-left-light
|
- state: inhand-left-light
|
||||||
|
|||||||
Reference in New Issue
Block a user