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 Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -6,36 +6,14 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.Light.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[Access(typeof(HandheldLightSystem))]
|
||||
public sealed class HandheldLightComponent : SharedHandheldLightComponent, IItemStatus
|
||||
{
|
||||
public byte? Level;
|
||||
public bool Activated;
|
||||
namespace Content.Client.Light.Components;
|
||||
|
||||
/// <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;
|
||||
|
||||
public Control MakeControl()
|
||||
{
|
||||
return new StatusControl(this);
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
public sealed class HandheldLightStatus : Control
|
||||
{
|
||||
private const float TimerCycle = 1;
|
||||
|
||||
private readonly HandheldLightComponent _parent;
|
||||
private readonly PanelContainer[] _sections = new PanelContainer[StatusLevels - 1];
|
||||
private readonly PanelContainer[] _sections = new PanelContainer[HandheldLightComponent.StatusLevels - 1];
|
||||
|
||||
private float _timer;
|
||||
|
||||
@@ -49,7 +27,7 @@ namespace Content.Client.Light.Components
|
||||
BackgroundColor = Color.Black
|
||||
};
|
||||
|
||||
public StatusControl(HandheldLightComponent parent)
|
||||
public HandheldLightStatus(HandheldLightComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
|
||||
@@ -104,5 +82,3 @@ namespace Content.Client.Light.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,21 @@
|
||||
using Content.Client.Items.Systems;
|
||||
using Content.Client.Items;
|
||||
using Content.Client.Light.Components;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Light;
|
||||
using Content.Shared.Light.Component;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Light;
|
||||
|
||||
public sealed class HandheldLightSystem : EntitySystem
|
||||
public sealed class HandheldLightSystem : SharedHandheldLightSystem
|
||||
{
|
||||
[Dependency] private readonly ItemSystem _itemSys = default!;
|
||||
|
||||
public override void 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)
|
||||
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);
|
||||
}
|
||||
args.Controls.Add(new HandheldLightStatus(component));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Content.Shared.Light;
|
||||
using Content.Shared.Light.Component;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Clothing.Components;
|
||||
using Content.Server.Inventory;
|
||||
using Content.Shared.Clothing.EntitySystems;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item;
|
||||
using NUnit.Framework;
|
||||
@@ -37,7 +38,8 @@ namespace Content.IntegrationTests.Tests
|
||||
|
||||
var child = entMgr.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
var item = entMgr.AddComponent<ClothingComponent>(child);
|
||||
item.Slots = SlotFlags.HEAD;
|
||||
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ClothingSystem>().SetSlots(item.Owner, SlotFlags.HEAD, item);
|
||||
|
||||
// Equip item.
|
||||
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.Light.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Light.Component;
|
||||
using Content.Shared.Light;
|
||||
using Content.Shared.Rounding;
|
||||
using Content.Shared.Toggleable;
|
||||
using Content.Shared.Verbs;
|
||||
@@ -23,7 +21,7 @@ using Robust.Shared.Utility;
|
||||
namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class HandheldLightSystem : EntitySystem
|
||||
public sealed class HandheldLightSystem : SharedHandheldLightSystem
|
||||
{
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
@@ -38,7 +36,6 @@ namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandheldLightComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<HandheldLightComponent, ComponentRemove>(OnRemove);
|
||||
SubscribeLocalEvent<HandheldLightComponent, ComponentGetState>(OnGetState);
|
||||
|
||||
@@ -99,7 +96,7 @@ namespace Content.Server.Light.EntitySystems
|
||||
|
||||
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)
|
||||
@@ -113,15 +110,7 @@ namespace Content.Server.Light.EntitySystems
|
||||
if (MathHelper.CloseToPercent(battery.CurrentCharge, 0) || component.Wattage > battery.CurrentCharge)
|
||||
return 0;
|
||||
|
||||
return (byte?) ContentHelpers.RoundToNearestLevels(battery.CurrentCharge / battery.MaxCharge * 255, 255, SharedHandheldLightComponent.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);
|
||||
return (byte?) ContentHelpers.RoundToNearestLevels(battery.CurrentCharge / battery.MaxCharge * 255, 255, HandheldLightComponent.StatusLevels);
|
||||
}
|
||||
|
||||
private void OnRemove(EntityUid uid, HandheldLightComponent component, ComponentRemove args)
|
||||
@@ -201,19 +190,9 @@ namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
if (!component.Activated) return false;
|
||||
|
||||
component.Activated = false;
|
||||
if (component.ToggleAction != null)
|
||||
_actionSystem.SetToggled(component.ToggleAction, false);
|
||||
SetActivated(component.Owner, false, component, makeNoise);
|
||||
component.Level = null;
|
||||
_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;
|
||||
}
|
||||
|
||||
@@ -239,17 +218,9 @@ namespace Content.Server.Light.EntitySystems
|
||||
return false;
|
||||
}
|
||||
|
||||
component.Activated = true;
|
||||
if (component.ToggleAction != null)
|
||||
_actionSystem.SetToggled(component.ToggleAction, true);
|
||||
SetActivated(component.Owner, true, component, true);
|
||||
_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;
|
||||
}
|
||||
|
||||
@@ -288,10 +259,10 @@ namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
var level = GetLevel(comp);
|
||||
|
||||
if (level == comp.LastLevel)
|
||||
if (level == comp.Level)
|
||||
return;
|
||||
|
||||
comp.LastLevel = level;
|
||||
comp.Level = level;
|
||||
Dirty(comp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Clothing.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Smoking;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Server.GameObjects;
|
||||
@@ -26,6 +27,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly ClothingSystem _clothing = default!;
|
||||
[Dependency] private readonly SharedItemSystem _items = default!;
|
||||
|
||||
private const float UpdateTimer = 3f;
|
||||
|
||||
@@ -61,6 +63,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
};
|
||||
|
||||
_clothing.SetEquippedPrefix(uid, newState, clothing);
|
||||
_items.SetHeldPrefix(uid, newState);
|
||||
|
||||
if (state == SmokableState.Lit)
|
||||
_active.Add(uid);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Clothing.EntitySystems;
|
||||
using Content.Shared.Clothing.EntitySystems;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -10,9 +10,11 @@ namespace Content.Shared.Clothing.Components;
|
||||
/// This handles entities which can be equipped.
|
||||
/// </summary>
|
||||
[NetworkedComponent]
|
||||
[Access(typeof(ClothingSystem), typeof(InventorySystem))]
|
||||
public abstract class SharedClothingComponent : Component
|
||||
{
|
||||
[DataField("clothingVisuals")]
|
||||
[Access(typeof(ClothingSystem), typeof(InventorySystem), Other = AccessPermissions.ReadExecute)] // TODO remove execute permissions.
|
||||
public Dictionary<string, List<SharedSpriteComponent.PrototypeLayerData>> ClothingVisuals = new();
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -21,6 +23,7 @@ public abstract class SharedClothingComponent : Component
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("slots", required: true)]
|
||||
[Access(typeof(ClothingSystem), typeof(InventorySystem), Other = AccessPermissions.ReadExecute)]
|
||||
public SlotFlags Slots = SlotFlags.NONE;
|
||||
|
||||
[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;
|
||||
|
||||
namespace Content.Shared.Clothing.EntitySystems;
|
||||
|
||||
public sealed class ClothingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedItemSystem _itemSys = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -21,17 +25,30 @@ public sealed class ClothingSystem : EntitySystem
|
||||
private void OnHandleState(EntityUid uid, SharedClothingComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is ClothingComponentState state)
|
||||
component.EquippedPrefix = state.EquippedPrefix;
|
||||
SetEquippedPrefix(uid, state.EquippedPrefix, component);
|
||||
}
|
||||
|
||||
#region Public API
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Item;
|
||||
@@ -8,11 +9,13 @@ namespace Content.Shared.Item;
|
||||
/// like backpacks.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
[Access(typeof(SharedItemSystem))]
|
||||
public sealed class ItemComponent : Component
|
||||
{
|
||||
[Access(typeof(SharedItemSystem), Other = AccessPermissions.ReadExecute)]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("size")]
|
||||
[Access(typeof(SharedItemSystem), Other = AccessPermissions.ReadExecute)]
|
||||
public int Size = 5;
|
||||
|
||||
[DataField("inhandVisuals")]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Verbs;
|
||||
@@ -38,7 +38,10 @@ public abstract class SharedItemSystem : EntitySystem
|
||||
|
||||
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;
|
||||
|
||||
component.HeldPrefix = heldPrefix;
|
||||
@@ -62,7 +65,7 @@ public abstract class SharedItemSystem : EntitySystem
|
||||
return;
|
||||
|
||||
component.Size = state.Size;
|
||||
component.HeldPrefix = state.HeldPrefix;
|
||||
SetHeldPrefix(uid, state.HeldPrefix, component);
|
||||
}
|
||||
|
||||
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" ]
|
||||
- type: HandheldLight
|
||||
addPrefix: false
|
||||
- type: ToggleableLightVisuals
|
||||
clothingVisuals:
|
||||
head:
|
||||
- state: equipped-head-light
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
startingItem: PowerCellHigh
|
||||
- type: HandheldLight
|
||||
addPrefix: false
|
||||
- type: ToggleableLightVisuals
|
||||
inhandVisuals:
|
||||
left:
|
||||
- state: inhand-left-light
|
||||
|
||||
Reference in New Issue
Block a user