Power Cell Refactor (#5943)
Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>
This commit is contained in:
@@ -75,12 +75,10 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
[DataField("insertSound")]
|
||||
public SoundSpecifier? InsertSound;
|
||||
// maybe default to /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg ??
|
||||
public SoundSpecifier InsertSound = new SoundPathSpecifier("/Audio/Weapons/Guns/MagIn/revolver_magin.ogg");
|
||||
|
||||
[DataField("ejectSound")]
|
||||
public SoundSpecifier? EjectSound;
|
||||
// maybe default to /Audio/Machines/id_swipe.ogg?
|
||||
public SoundSpecifier EjectSound = new SoundPathSpecifier("/Audio/Weapons/Guns/MagOut/revolver_magout.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// Options used for playing the insert/eject sounds.
|
||||
@@ -98,6 +96,9 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
[DataField("name")]
|
||||
public string Name = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The entity prototype that is spawned into this slot on map init.
|
||||
/// </summary>
|
||||
[DataField("startingItem", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string? StartingItem;
|
||||
|
||||
@@ -136,15 +137,15 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
public bool EjectOnUse = false;
|
||||
|
||||
/// <summary>
|
||||
/// Override the insert verb text. Defaults to [insert category] -> [item-name]. If not null, the verb will
|
||||
/// not be given a category.
|
||||
/// Override the insert verb text. Defaults to using the slot's name (if specified) or the name of the
|
||||
/// targeted item. If specified, the verb will not be added to the default insert verb category.
|
||||
/// </summary>
|
||||
[DataField("insertVerbText")]
|
||||
public string? InsertVerbText;
|
||||
|
||||
/// <summary>
|
||||
/// Override the insert verb text. Defaults to [eject category] -> [item-name]. If not null, the verb will
|
||||
/// not be given a category.
|
||||
/// Override the eject verb text. Defaults to using the slot's name (if specified) or the name of the
|
||||
/// targeted item. If specified, the verb will not be added to the default eject verb category
|
||||
/// </summary>
|
||||
[DataField("ejectVerbText")]
|
||||
public string? EjectVerbText;
|
||||
|
||||
@@ -242,10 +242,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should also check ContainerSlot.CanInsert, but that prevents swapping interactions. Given that
|
||||
// ContainerSlot.CanInsert gets called when the item is actually inserted anyways, we can just get away with
|
||||
// fudging CanInsert and not performing those checks.
|
||||
return true;
|
||||
return slot.ContainerSlot.CanInsertIfEmpty(usedUid, EntityManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -302,6 +299,15 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
#endregion
|
||||
|
||||
#region Eject
|
||||
|
||||
public bool CanEject(ItemSlot slot)
|
||||
{
|
||||
if (slot.Locked || slot.Item == null)
|
||||
return false;
|
||||
|
||||
return slot.ContainerSlot.CanRemove(slot.Item.Value, EntityManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eject an item into a slot. This does not perform checks (e.g., is the slot locked?), so you should
|
||||
/// probably just use <see cref="TryEject"/> instead.
|
||||
@@ -324,11 +330,11 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
{
|
||||
item = null;
|
||||
|
||||
if (slot.Locked || slot.Item == null)
|
||||
if (!CanEject(slot))
|
||||
return false;
|
||||
|
||||
item = slot.Item;
|
||||
Eject(uid, slot, item.Value, user, excludeUserAudio);
|
||||
Eject(uid, slot, item!.Value, user, excludeUserAudio);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -381,7 +387,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
|
||||
foreach (var slot in itemSlots.Slots.Values)
|
||||
{
|
||||
if (slot.Locked || !slot.HasItem)
|
||||
if (!CanEject(slot))
|
||||
continue;
|
||||
|
||||
if (slot.EjectOnInteract)
|
||||
@@ -421,7 +427,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
{
|
||||
foreach (var slot in itemSlots.Slots.Values)
|
||||
{
|
||||
if (!slot.EjectOnInteract || slot.Locked || !slot.HasItem)
|
||||
if (!slot.EjectOnInteract || !CanEject(slot))
|
||||
continue;
|
||||
|
||||
var verbSubject = slot.Name != string.Empty
|
||||
|
||||
@@ -9,8 +9,6 @@ namespace Content.Shared.Light.Component
|
||||
[ComponentProtoName("HandheldLight")]
|
||||
public abstract class SharedHandheldLightComponent : Robust.Shared.GameObjects.Component
|
||||
{
|
||||
protected abstract bool HasCell { get; }
|
||||
|
||||
public const int StatusLevels = 6;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
41
Content.Shared/PowerCell/Components/PowerCellComponent.cs
Normal file
41
Content.Shared/PowerCell/Components/PowerCellComponent.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.PowerCell;
|
||||
|
||||
/// <summary>
|
||||
/// This component enables power-cell related interactions (e.g., entity white-lists, cell sizes, examine, rigging).
|
||||
/// The actual power functionality is provided by the server-side BatteryComponent.
|
||||
/// </summary>
|
||||
[NetworkedComponent]
|
||||
[RegisterComponent]
|
||||
[ComponentProtoName("PowerCell")]
|
||||
public sealed class PowerCellComponent : Component
|
||||
{
|
||||
public const string SolutionName = "powerCell";
|
||||
public const int PowerCellVisualsLevels = 4;
|
||||
|
||||
[DataField("cellSize")]
|
||||
public PowerCellSize CellSize = PowerCellSize.Small;
|
||||
|
||||
// Not networked to clients
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool IsRigged { get; set; }
|
||||
}
|
||||
|
||||
public enum PowerCellSize
|
||||
{
|
||||
Small = 0,
|
||||
Medium = 1,
|
||||
Large = 2
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum PowerCellVisuals
|
||||
{
|
||||
ChargeLevel
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.PowerCell.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class PowerCellSlotComponent : Component
|
||||
{
|
||||
public override string Name => "PowerCellSlot";
|
||||
|
||||
/// <summary>
|
||||
/// What size of cell fits into this component.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("slotSize")]
|
||||
public PowerCellSize SlotSize { get; set; } = PowerCellSize.Small;
|
||||
|
||||
/// <summary>
|
||||
/// The actual item-slot that contains the cell. Allows all the interaction logic to be handled by <see cref="ItemSlotsSystem"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Given that <see cref="PowerCellSystem"/> needs to verify that a given cell has the correct cell-size before
|
||||
/// inserting anyways, there is no need to specify a separate entity whitelist. In this slot's yaml definition.
|
||||
/// </remarks>
|
||||
[DataField("cellSlot")]
|
||||
public ItemSlot CellSlot = new();
|
||||
|
||||
/// <summary>
|
||||
/// Name of the item-slot used to store cells. Determines the eject/insert verb text. E.g., "Eject > Power cell".
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is simply used provide a default value for <see cref="CellSlot.Name"/>. If this string is empty or
|
||||
/// whitespace, the verb will instead use the full name of any cell (e.g., "eject > small super-capacity power
|
||||
/// cell").
|
||||
/// </remarks>
|
||||
[DataField("slotName")]
|
||||
public readonly string SlotName = "power-cell-slot-component-slot-name-default"; // gets Loc.GetString()-ed by ItemSlotsSystem
|
||||
|
||||
/// <summary>
|
||||
/// True if we don't want a cell inserted during map init. If a starting item is defined
|
||||
/// in the <see cref="CellSlot"/> yaml definition, that always takes precedence.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If false, the cell will start with a standard cell with a matching cell-size.
|
||||
/// </remarks>
|
||||
[DataField("startEmpty")]
|
||||
public bool StartEmpty = false;
|
||||
|
||||
/// <summary>
|
||||
/// Descriptive text to add to add when examining an entity with a cell slot. If empty or whitespace, will not add
|
||||
/// any text.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("descFormatString")]
|
||||
public string? DescFormatString { get; set; } = "power-cell-slot-component-description-default";
|
||||
|
||||
/// <summary>
|
||||
/// Can this entity be inserted directly into a charging station? If false, you need to manually remove the power
|
||||
/// cell and recharge it separately.
|
||||
/// </summary>
|
||||
[DataField("fitsInCharger")]
|
||||
public bool FitsInCharger = true;
|
||||
}
|
||||
|
||||
public class PowerCellChangedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly bool Ejected;
|
||||
|
||||
public PowerCellChangedEvent(bool ejected)
|
||||
{
|
||||
Ejected = ejected;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.PowerCell
|
||||
{
|
||||
public static class SharedPowerCell
|
||||
{
|
||||
public const int PowerCellVisualsLevels = 4;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum PowerCellVisuals
|
||||
{
|
||||
ChargeLevel
|
||||
}
|
||||
}
|
||||
109
Content.Shared/PowerCell/SharedPowerCellSystem.cs
Normal file
109
Content.Shared/PowerCell/SharedPowerCellSystem.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using System;
|
||||
|
||||
namespace Content.Shared.PowerCell;
|
||||
|
||||
public abstract class SharedPowerCellSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, ComponentInit>(OnCellSlotInit);
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, ComponentRemove>(OnCellSlotRemove);
|
||||
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, ExaminedEvent>(OnSlotExamined);
|
||||
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, EntInsertedIntoContainerMessage>(OnCellInserted);
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, EntRemovedFromContainerMessage>(OnCellRemoved);
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, ContainerIsInsertingAttemptEvent>(OnCellInsertAttempt);
|
||||
}
|
||||
|
||||
private void OnCellInsertAttempt(EntityUid uid, PowerCellSlotComponent component, ContainerIsInsertingAttemptEvent args)
|
||||
{
|
||||
if (!component.Initialized)
|
||||
return;
|
||||
|
||||
if (args.Container.ID != component.CellSlot.ID)
|
||||
return;
|
||||
|
||||
if (!TryComp(args.EntityUid, out PowerCellComponent? cell) || cell.CellSize != component.SlotSize)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCellInserted(EntityUid uid, PowerCellSlotComponent component, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (!component.Initialized)
|
||||
return;
|
||||
|
||||
if (args.Container.ID != component.CellSlot.ID)
|
||||
return;
|
||||
|
||||
RaiseLocalEvent(uid, new PowerCellChangedEvent(false), false);
|
||||
}
|
||||
|
||||
private void OnCellRemoved(EntityUid uid, PowerCellSlotComponent component, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != component.CellSlot.ID)
|
||||
return;
|
||||
|
||||
RaiseLocalEvent(uid, new PowerCellChangedEvent(true), false);
|
||||
}
|
||||
|
||||
private void OnCellSlotInit(EntityUid uid, PowerCellSlotComponent component, ComponentInit args)
|
||||
{
|
||||
_itemSlotsSystem.AddItemSlot(uid, "cellslot_cell_container", component.CellSlot);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(component.CellSlot.Name) &&
|
||||
!string.IsNullOrWhiteSpace(component.SlotName))
|
||||
{
|
||||
component.CellSlot.Name = component.SlotName;
|
||||
}
|
||||
|
||||
if (component.StartEmpty)
|
||||
return;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(component.CellSlot.StartingItem))
|
||||
return;
|
||||
|
||||
// set default starting cell based on cell-type
|
||||
component.CellSlot.StartingItem = component.SlotSize switch
|
||||
{
|
||||
PowerCellSize.Small => "PowerCellSmallStandard",
|
||||
PowerCellSize.Medium => "PowerCellMediumStandard",
|
||||
PowerCellSize.Large => "PowerCellLargeStandard",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
private void OnCellSlotRemove(EntityUid uid, PowerCellSlotComponent component, ComponentRemove args)
|
||||
{
|
||||
_itemSlotsSystem.RemoveItemSlot(uid, component.CellSlot);
|
||||
}
|
||||
|
||||
private void OnSlotExamined(EntityUid uid, PowerCellSlotComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (!args.IsInDetailsRange || string.IsNullOrWhiteSpace(component.DescFormatString))
|
||||
return;
|
||||
|
||||
var sizeText = Loc.GetString(component.SlotSize switch
|
||||
{
|
||||
PowerCellSize.Small => "power-cell-slot-component-description-size-small",
|
||||
PowerCellSize.Medium => "power-cell-slot-component-description-size-medium",
|
||||
PowerCellSize.Large => "power-cell-slot-component-description-size-large",
|
||||
_ => "???"
|
||||
});
|
||||
|
||||
args.PushMarkup(Loc.GetString(component.DescFormatString, ("size", sizeText)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user