Borgs (#18136)
* Laws * positronic brain and PAI rewrite * MMI * MMI pt. 2 * borg brain transfer * Roleban support, Borg job (WIP), the end of mind shenaniganry * battery drain, item slot cleanup, alerts * visuals * fix this pt1 * fix this pt2 * Modules, Lingering Stacks, Better borg flashlight * Start on UI, fix battery alerts, expand activation/deactivation, low movement speed on no power. * sprotes * no zombie borgs * oh fuck yeah i love a good relay * charger * fix the tiniest of sprite issues * adjustable names * a functional UI???? * foobar * more modules * this shit for some reason * upstream * genericize selectable borg modules * upstream again * holy fucking shit * i love christ * proper construction * da job * AA borgs * and boom more shit * admin logs * laws redux * ok just do this rq * oh boy that looks like modules * oh shit research * testos passo * so much shit holy fuck * fuckit we SHIP * last minute snags * should've gotten me on a better day
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using Content.Shared.Access.Systems;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
|
||||
|
||||
namespace Content.Shared.Access.Components
|
||||
@@ -34,4 +35,16 @@ namespace Content.Shared.Access.Components
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct GetAccessTagsEvent(HashSet<string> Tags, IPrototypeManager PrototypeManager)
|
||||
{
|
||||
public void AddGroup(string group)
|
||||
{
|
||||
if (!PrototypeManager.TryIndex<AccessGroupPrototype>(group, out var groupPrototype))
|
||||
return;
|
||||
|
||||
Tags.UnionWith(groupPrototype.Tags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,13 @@ using Robust.Shared.GameStates;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Access.Systems;
|
||||
|
||||
public sealed class AccessReaderSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
@@ -272,23 +274,24 @@ public sealed class AccessReaderSystem : EntitySystem
|
||||
/// Try to find <see cref="AccessComponent"/> on this item
|
||||
/// or inside this item (if it's pda)
|
||||
/// </summary>
|
||||
private bool FindAccessTagsItem(EntityUid uid, [NotNullWhen(true)] out HashSet<string>? tags)
|
||||
private bool FindAccessTagsItem(EntityUid uid, out HashSet<string> tags)
|
||||
{
|
||||
tags = new();
|
||||
if (TryComp(uid, out AccessComponent? access))
|
||||
{
|
||||
tags = access.Tags;
|
||||
return true;
|
||||
tags.UnionWith(access.Tags);
|
||||
}
|
||||
|
||||
if (TryComp(uid, out PdaComponent? pda) &&
|
||||
pda.ContainedId is { Valid: true } id)
|
||||
{
|
||||
tags = EntityManager.GetComponent<AccessComponent>(id).Tags;
|
||||
return true;
|
||||
tags.UnionWith(EntityManager.GetComponent<AccessComponent>(id).Tags);
|
||||
}
|
||||
|
||||
tags = null;
|
||||
return false;
|
||||
var ev = new GetAccessTagsEvent(tags, _prototype);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
return tags.Count != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -15,5 +15,6 @@ public enum AlertCategory
|
||||
Piloting,
|
||||
Hunger,
|
||||
Thirst,
|
||||
Toxins
|
||||
Toxins,
|
||||
Battery
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace Content.Shared.Alert
|
||||
HumanCrit,
|
||||
HumanDead,
|
||||
HumanHealth,
|
||||
BorgBattery,
|
||||
BorgBatteryNone,
|
||||
PilotingShuttle,
|
||||
Peckish,
|
||||
Starving,
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Shared.Construction.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for construction which requires a set of
|
||||
/// entities with specific tags to be inserted into another entity.
|
||||
/// todo: in a pr that isn't 6k loc, combine this with MechAssemblyComponent
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class PartAssemblyComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// A dictionary of a set of parts to a list of tags for each assembly.
|
||||
/// </summary>
|
||||
[DataField("parts", required: true)]
|
||||
public Dictionary<string, List<string>> Parts = new();
|
||||
|
||||
/// <summary>
|
||||
/// The entry in <see cref="Parts"/> that is currently being worked on.
|
||||
/// </summary>
|
||||
[DataField("currentAssembly")]
|
||||
public string? CurrentAssembly;
|
||||
|
||||
/// <summary>
|
||||
/// The container where the parts are stored
|
||||
/// </summary>
|
||||
[DataField("containerId")]
|
||||
public string ContainerId = "part-container";
|
||||
|
||||
/// <summary>
|
||||
/// The container that stores all of the parts when
|
||||
/// they're being assembled.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Container PartsContainer = default!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a valid part is inserted into the part assembly.
|
||||
/// </summary>
|
||||
public sealed class PartAssemblyPartInsertedEvent
|
||||
{
|
||||
|
||||
}
|
||||
147
Content.Shared/Construction/PartAssemblySystem.cs
Normal file
147
Content.Shared/Construction/PartAssemblySystem.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Shared.Construction;
|
||||
|
||||
/// <summary>
|
||||
/// This handles <see cref="PartAssemblyComponent"/>
|
||||
/// </summary>
|
||||
public sealed class PartAssemblySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<PartAssemblyComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<PartAssemblyComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<PartAssemblyComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, PartAssemblyComponent component, ComponentInit args)
|
||||
{
|
||||
component.PartsContainer = _container.EnsureContainer<Container>(uid, component.ContainerId);
|
||||
}
|
||||
|
||||
private void OnInteractUsing(EntityUid uid, PartAssemblyComponent component, InteractUsingEvent args)
|
||||
{
|
||||
if (!TryInsertPart(args.Used, uid, component))
|
||||
return;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnEntRemoved(EntityUid uid, PartAssemblyComponent component, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != component.ContainerId)
|
||||
return;
|
||||
if (component.PartsContainer.ContainedEntities.Count != 0)
|
||||
return;
|
||||
component.CurrentAssembly = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to insert a part into the current assembly, starting one if there is none.
|
||||
/// </summary>
|
||||
public bool TryInsertPart(EntityUid part, EntityUid uid, PartAssemblyComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
string? assemblyId = null;
|
||||
assemblyId ??= component.CurrentAssembly;
|
||||
|
||||
if (assemblyId == null)
|
||||
{
|
||||
foreach (var (id, tags) in component.Parts)
|
||||
{
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (!_tag.HasTag(part, tag))
|
||||
continue;
|
||||
assemblyId = id;
|
||||
break;
|
||||
}
|
||||
|
||||
if (assemblyId != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (assemblyId == null)
|
||||
return false;
|
||||
|
||||
if (!IsPartValid(uid, part, assemblyId, component))
|
||||
return false;
|
||||
|
||||
component.CurrentAssembly = assemblyId;
|
||||
component.PartsContainer.Insert(part);
|
||||
var ev = new PartAssemblyPartInsertedEvent();
|
||||
RaiseLocalEvent(uid, ev);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given entity is a valid item for the assembly.
|
||||
/// </summary>
|
||||
public bool IsPartValid(EntityUid uid, EntityUid part, string assemblyId, PartAssemblyComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return true;
|
||||
|
||||
if (!component.Parts.TryGetValue(assemblyId, out var tags))
|
||||
return false;
|
||||
|
||||
var openTags = new List<string>(tags);
|
||||
var contained = new List<EntityUid>(component.PartsContainer.ContainedEntities);
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
foreach (var ent in component.PartsContainer.ContainedEntities)
|
||||
{
|
||||
if (!contained.Contains(ent) || !_tag.HasTag(ent, tag))
|
||||
continue;
|
||||
openTags.Remove(tag);
|
||||
contained.Remove(ent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var tag in openTags)
|
||||
{
|
||||
if (_tag.HasTag(part, tag))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsAssemblyFinished(EntityUid uid, string assemblyId, PartAssemblyComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return true;
|
||||
|
||||
if (!component.Parts.TryGetValue(assemblyId, out var parts))
|
||||
return false;
|
||||
|
||||
var contained = new List<EntityUid>(component.PartsContainer.ContainedEntities);
|
||||
foreach (var tag in parts)
|
||||
{
|
||||
var valid = false;
|
||||
foreach (var ent in new List<EntityUid>(contained))
|
||||
{
|
||||
if (!_tag.HasTag(ent, tag))
|
||||
continue;
|
||||
valid = true;
|
||||
contained.Remove(ent);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,11 @@ namespace Content.Shared.Construction.Steps
|
||||
return typeof(TemperatureConstructionGraphStep);
|
||||
}
|
||||
|
||||
if (node.Has("assemblyId") || node.Has("guideString"))
|
||||
{
|
||||
return typeof(PartAssemblyConstructionGraphStep);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Examine;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Shared.Construction.Steps;
|
||||
|
||||
[DataDefinition]
|
||||
public sealed class PartAssemblyConstructionGraphStep : ConstructionGraphStep
|
||||
{
|
||||
/// <summary>
|
||||
/// A valid ID on <see cref="PartAssemblyComponent"/>'s dictionary of strings to part lists.
|
||||
/// </summary>
|
||||
[DataField("assemblyId")]
|
||||
public string AssemblyId = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// A localization string used for
|
||||
/// </summary>
|
||||
[DataField("guideString")]
|
||||
public string GuideString = "construction-guide-condition-part-assembly";
|
||||
|
||||
public bool Condition(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
return entityManager.System<PartAssemblySystem>().IsAssemblyFinished(uid, AssemblyId);
|
||||
}
|
||||
|
||||
public override void DoExamine(ExaminedEvent args)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString(GuideString));
|
||||
}
|
||||
|
||||
public override ConstructionGuideEntry GenerateGuideEntry()
|
||||
{
|
||||
return new ConstructionGuideEntry
|
||||
{
|
||||
Localization = GuideString,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -233,4 +233,16 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
Priority = other.Priority;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on the slot entity and the item being inserted to determine if an item can be inserted into an item slot.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ItemSlotInsertAttemptEvent(EntityUid SlotEntity, EntityUid Item, EntityUid? User, ItemSlot Slot, bool Cancelled = false);
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on the slot entity and the item being inserted to determine if an item can be ejected from an item slot.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ItemSlotEjectAttemptEvent(EntityUid SlotEntity, EntityUid Item, EntityUid? User, ItemSlot Slot, bool Cancelled = false);
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
if (!slot.InsertOnInteract)
|
||||
continue;
|
||||
|
||||
if (!CanInsert(uid, args.Used, slot, swap: slot.Swap, popup: args.User))
|
||||
if (!CanInsert(uid, args.Used, args.User, slot, swap: slot.Swap, popup: args.User))
|
||||
continue;
|
||||
|
||||
// Drop the held item onto the floor. Return if the user cannot drop.
|
||||
@@ -244,7 +244,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
/// If a popup entity is given, and if the item slot is set to generate a popup message when it fails to
|
||||
/// pass the whitelist, then this will generate a popup.
|
||||
/// </remarks>
|
||||
public bool CanInsert(EntityUid uid, EntityUid usedUid, ItemSlot slot, bool swap = false, EntityUid? popup = null)
|
||||
public bool CanInsert(EntityUid uid, EntityUid usedUid, EntityUid? user, ItemSlot slot, bool swap = false, EntityUid? popup = null)
|
||||
{
|
||||
if (slot.Locked)
|
||||
return false;
|
||||
@@ -259,6 +259,12 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
return false;
|
||||
}
|
||||
|
||||
var ev = new ItemSlotInsertAttemptEvent(uid, usedUid, user, slot);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
RaiseLocalEvent(usedUid, ref ev);
|
||||
if (ev.Cancelled)
|
||||
return false;
|
||||
|
||||
return slot.ContainerSlot?.CanInsertIfEmpty(usedUid, EntityManager) ?? false;
|
||||
}
|
||||
|
||||
@@ -283,7 +289,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
/// <returns>False if failed to insert item</returns>
|
||||
public bool TryInsert(EntityUid uid, ItemSlot slot, EntityUid item, EntityUid? user)
|
||||
{
|
||||
if (!CanInsert(uid, item, slot))
|
||||
if (!CanInsert(uid, item, user, slot))
|
||||
return false;
|
||||
|
||||
Insert(uid, slot, item, user);
|
||||
@@ -303,7 +309,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
if (hands.ActiveHand?.HeldEntity is not EntityUid held)
|
||||
return false;
|
||||
|
||||
if (!CanInsert(uid, held, slot))
|
||||
if (!CanInsert(uid, held, user, slot))
|
||||
return false;
|
||||
|
||||
// hands.Drop(item) checks CanDrop action blocker
|
||||
@@ -317,11 +323,17 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
|
||||
#region Eject
|
||||
|
||||
public bool CanEject(ItemSlot slot)
|
||||
public bool CanEject(EntityUid uid, EntityUid? user, ItemSlot slot)
|
||||
{
|
||||
if (slot.Locked || slot.Item == null)
|
||||
return false;
|
||||
|
||||
var ev = new ItemSlotEjectAttemptEvent(uid, slot.Item.Value, user, slot);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
RaiseLocalEvent(slot.Item.Value, ref ev);
|
||||
if (ev.Cancelled)
|
||||
return false;
|
||||
|
||||
return slot.ContainerSlot?.CanRemove(slot.Item.Value, EntityManager) ?? false;
|
||||
}
|
||||
|
||||
@@ -352,7 +364,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
item = null;
|
||||
|
||||
// This handles logic with the slot itself
|
||||
if (!CanEject(slot))
|
||||
if (!CanEject(uid, user, slot))
|
||||
return false;
|
||||
|
||||
item = slot.Item;
|
||||
@@ -418,7 +430,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
foreach (var slot in itemSlots.Slots.Values)
|
||||
{
|
||||
// Disable slot insert if InsertOnInteract is true
|
||||
if (slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, slot))
|
||||
if (slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, args.User, slot))
|
||||
continue;
|
||||
|
||||
var verbSubject = slot.Name != string.Empty
|
||||
@@ -467,7 +479,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
// alt-click verb, there will be a "Take item" primary interaction verb.
|
||||
continue;
|
||||
|
||||
if (!CanEject(slot))
|
||||
if (!CanEject(uid, args.User, slot))
|
||||
continue;
|
||||
|
||||
if (!_actionBlockerSystem.CanPickup(args.User, slot.Item!.Value))
|
||||
@@ -506,7 +518,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
// If there are any slots that eject on left-click, add a "Take <item>" verb.
|
||||
foreach (var slot in itemSlots.Slots.Values)
|
||||
{
|
||||
if (!slot.EjectOnInteract || !CanEject(slot))
|
||||
if (!slot.EjectOnInteract || !CanEject(uid, args.User, slot))
|
||||
continue;
|
||||
|
||||
if (!_actionBlockerSystem.CanPickup(args.User, slot.Item!.Value))
|
||||
@@ -514,7 +526,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
|
||||
var verbSubject = slot.Name != string.Empty
|
||||
? Loc.GetString(slot.Name)
|
||||
: EntityManager.GetComponent<MetaDataComponent>(slot.Item!.Value).EntityName ?? string.Empty;
|
||||
: Name(slot.Item!.Value);
|
||||
|
||||
InteractionVerb takeVerb = new();
|
||||
takeVerb.IconEntity = slot.Item;
|
||||
@@ -535,7 +547,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
|
||||
foreach (var slot in itemSlots.Slots.Values)
|
||||
{
|
||||
if (!slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, slot))
|
||||
if (!slot.InsertOnInteract || !CanInsert(uid, args.Using.Value, args.User, slot))
|
||||
continue;
|
||||
|
||||
var verbSubject = slot.Name != string.Empty
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Interaction.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for entities which cannot move or interact in any way.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class BlockMovementComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Movement.Events;
|
||||
|
||||
namespace Content.Shared.Interaction;
|
||||
|
||||
public partial class SharedInteractionSystem
|
||||
{
|
||||
public void InitializeBlocking()
|
||||
{
|
||||
SubscribeLocalEvent<BlockMovementComponent, UpdateCanMoveEvent>(OnMoveAttempt);
|
||||
SubscribeLocalEvent<BlockMovementComponent, UseAttemptEvent>(CancelEvent);
|
||||
SubscribeLocalEvent<BlockMovementComponent, InteractionAttemptEvent>(CancelEvent);
|
||||
SubscribeLocalEvent<BlockMovementComponent, DropAttemptEvent>(CancelEvent);
|
||||
SubscribeLocalEvent<BlockMovementComponent, PickupAttemptEvent>(CancelEvent);
|
||||
SubscribeLocalEvent<BlockMovementComponent, ChangeDirectionAttemptEvent>(CancelEvent);
|
||||
|
||||
SubscribeLocalEvent<BlockMovementComponent, ComponentStartup>(OnBlockingStartup);
|
||||
SubscribeLocalEvent<BlockMovementComponent, ComponentShutdown>(OnBlockingShutdown);
|
||||
}
|
||||
|
||||
private void OnMoveAttempt(EntityUid uid, BlockMovementComponent component, UpdateCanMoveEvent args)
|
||||
{
|
||||
if (component.LifeStage > ComponentLifeStage.Running)
|
||||
return;
|
||||
|
||||
args.Cancel(); // no more scurrying around
|
||||
}
|
||||
|
||||
private void CancelEvent(EntityUid uid, BlockMovementComponent component, CancellableEntityEventArgs args)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnBlockingStartup(EntityUid uid, BlockMovementComponent component, ComponentStartup args)
|
||||
{
|
||||
_actionBlockerSystem.UpdateCanMove(uid);
|
||||
}
|
||||
|
||||
private void OnBlockingShutdown(EntityUid uid, BlockMovementComponent component, ComponentShutdown args)
|
||||
{
|
||||
_actionBlockerSystem.UpdateCanMove(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ namespace Content.Shared.Interaction
|
||||
.Register<SharedInteractionSystem>();
|
||||
|
||||
InitializeRelay();
|
||||
InitializeBlocking();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
|
||||
@@ -40,6 +40,13 @@ namespace Content.Shared.Light
|
||||
[DataField("toggleActionId", customTypeSerializer: typeof(PrototypeIdSerializer<InstantActionPrototype>))]
|
||||
public string ToggleActionId = "ToggleLight";
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the light can be toggled via standard interactions
|
||||
/// (alt verbs, using in hand, etc)
|
||||
/// </summary>
|
||||
[DataField("toggleOnInteract")]
|
||||
public bool ToggleOnInteract = true;
|
||||
|
||||
[DataField("toggleAction")]
|
||||
public InstantAction? ToggleAction;
|
||||
|
||||
|
||||
@@ -118,6 +118,9 @@ public abstract class SharedMaterialReclaimerSystem : EntitySystem
|
||||
if (component.Blacklist != null && component.Blacklist.IsValid(item))
|
||||
return false;
|
||||
|
||||
if (!_container.TryRemoveFromContainer(item))
|
||||
return false;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
_adminLog.Add(LogType.Action, LogImpact.High,
|
||||
|
||||
17
Content.Shared/Mind/MindVisuals.cs
Normal file
17
Content.Shared/Mind/MindVisuals.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Mind;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ToggleableGhostRoleVisuals : byte
|
||||
{
|
||||
Status
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ToggleableGhostRoleStatus : byte
|
||||
{
|
||||
Off,
|
||||
Searching,
|
||||
On
|
||||
}
|
||||
23
Content.Shared/NameIdentifier/NameIdentifierComponent.cs
Normal file
23
Content.Shared/NameIdentifier/NameIdentifierComponent.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.NameIdentifier;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class NameIdentifierComponent : Component
|
||||
{
|
||||
[DataField("group", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<NameIdentifierGroupPrototype>))]
|
||||
public string Group = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The randomly generated ID for this entity.
|
||||
/// </summary>
|
||||
[DataField("identifier"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||
public int Identifier = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The full name identifier for this entity.
|
||||
/// </summary>
|
||||
[DataField("fullIdentifier"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||
public string FullIdentifier = string.Empty;
|
||||
}
|
||||
@@ -16,6 +16,13 @@ namespace Content.Shared.PAI
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class PAIComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The last person who activated this PAI.
|
||||
/// Used for assigning the name.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public EntityUid? LastUser;
|
||||
|
||||
[DataField("midiAction", required: true, serverOnly: true)] // server only, as it uses a server-BUI event !type
|
||||
public InstantAction? MidiAction;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Movement;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.PAI
|
||||
{
|
||||
@@ -27,12 +24,6 @@ namespace Content.Shared.PAI
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PAIComponent, UseAttemptEvent>(OnUseAttempt);
|
||||
SubscribeLocalEvent<PAIComponent, InteractionAttemptEvent>(OnInteractAttempt);
|
||||
SubscribeLocalEvent<PAIComponent, DropAttemptEvent>(OnDropAttempt);
|
||||
SubscribeLocalEvent<PAIComponent, PickupAttemptEvent>(OnPickupAttempt);
|
||||
SubscribeLocalEvent<PAIComponent, UpdateCanMoveEvent>(OnMoveAttempt);
|
||||
SubscribeLocalEvent<PAIComponent, ChangeDirectionAttemptEvent>(OnChangeDirectionAttempt);
|
||||
|
||||
SubscribeLocalEvent<PAIComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<PAIComponent, ComponentShutdown>(OnShutdown);
|
||||
@@ -40,67 +31,15 @@ namespace Content.Shared.PAI
|
||||
|
||||
private void OnStartup(EntityUid uid, PAIComponent component, ComponentStartup args)
|
||||
{
|
||||
_blocker.UpdateCanMove(uid);
|
||||
if (component.MidiAction != null)
|
||||
_actionsSystem.AddAction(uid, component.MidiAction, null);
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, PAIComponent component, ComponentShutdown args)
|
||||
{
|
||||
_blocker.UpdateCanMove(uid);
|
||||
if (component.MidiAction != null)
|
||||
_actionsSystem.RemoveAction(uid, component.MidiAction);
|
||||
}
|
||||
|
||||
private void OnMoveAttempt(EntityUid uid, PAIComponent component, UpdateCanMoveEvent args)
|
||||
{
|
||||
if (component.LifeStage > ComponentLifeStage.Running)
|
||||
return;
|
||||
|
||||
args.Cancel(); // no more scurrying around on lil robot legs.
|
||||
}
|
||||
|
||||
private void OnChangeDirectionAttempt(EntityUid uid, PAIComponent component, ChangeDirectionAttemptEvent args)
|
||||
{
|
||||
// PAIs can't rotate, but decapitated heads and sentient crowbars can, life isn't fair. Seriously though, why
|
||||
// tf does this have to be actively blocked, surely this should just not be blanket enabled for any player
|
||||
// controlled entity. Same goes for moving really.
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnUseAttempt(EntityUid uid, PAIComponent component, UseAttemptEvent args)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnInteractAttempt(EntityUid uid, PAIComponent component, InteractionAttemptEvent args)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnDropAttempt(EntityUid uid, PAIComponent component, DropAttemptEvent args)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnPickupAttempt(EntityUid uid, PAIComponent component, PickupAttemptEvent args)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum PAIVisuals : byte
|
||||
{
|
||||
Status
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum PAIStatus : byte
|
||||
{
|
||||
Off,
|
||||
Searching,
|
||||
On
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
57
Content.Shared/Silicons/Borgs/BorgUI.cs
Normal file
57
Content.Shared/Silicons/Borgs/BorgUI.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Silicons.Borgs;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum BorgUiKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BorgBuiState : BoundUserInterfaceState
|
||||
{
|
||||
public float ChargePercent;
|
||||
|
||||
public bool HasBattery;
|
||||
|
||||
public BorgBuiState(float chargePercent, bool hasBattery)
|
||||
{
|
||||
ChargePercent = chargePercent;
|
||||
HasBattery = hasBattery;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BorgEjectBrainBuiMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BorgEjectBatteryBuiMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BorgSetNameBuiMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public BorgSetNameBuiMessage(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BorgRemoveModuleBuiMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public EntityUid Module;
|
||||
|
||||
public BorgRemoveModuleBuiMessage(EntityUid module)
|
||||
{
|
||||
Module = module;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Silicons.Borgs.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for brains and mind receptacles
|
||||
/// that can be inserted into a borg to transfer a mind.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))]
|
||||
public sealed class BorgBrainComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
126
Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs
Normal file
126
Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Shared.Silicons.Borgs.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for the core body of a borg. This manages a borg's
|
||||
/// "brain", legs, modules, and battery. Essentially the master component
|
||||
/// for borg logic.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem)), AutoGenerateComponentState]
|
||||
public sealed partial class BorgChassisComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not the borg currently has a player occupying it
|
||||
/// </summary>
|
||||
[DataField("hasPlayer")]
|
||||
public bool HasPlayer;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the borg is activated, meaning it has access to modules and a heightened movement speed
|
||||
/// </summary>
|
||||
[DataField("activated"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||
public bool Activated;
|
||||
|
||||
#region Brain
|
||||
/// <summary>
|
||||
/// A whitelist for which entities count as valid brains
|
||||
/// </summary>
|
||||
[DataField("brainWhitelist")]
|
||||
public EntityWhitelist? BrainWhitelist;
|
||||
|
||||
/// <summary>
|
||||
/// The container ID for the brain
|
||||
/// </summary>
|
||||
[DataField("brainContainerId")]
|
||||
public string BrainContainerId = "borg_brain";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public ContainerSlot BrainContainer = default!;
|
||||
|
||||
public EntityUid? BrainEntity => BrainContainer.ContainedEntity;
|
||||
|
||||
/// <summary>
|
||||
/// A brain entity that fills the <see cref="BrainContainer"/> on roundstart
|
||||
/// </summary>
|
||||
[DataField("startingBrain", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string? StartingBrain;
|
||||
#endregion
|
||||
|
||||
#region Modules
|
||||
/// <summary>
|
||||
/// A whitelist for what types of modules can be installed into this borg
|
||||
/// </summary>
|
||||
[DataField("moduleWhitelist")]
|
||||
public EntityWhitelist? ModuleWhitelist;
|
||||
|
||||
/// <summary>
|
||||
/// How many modules can be installed in this borg
|
||||
/// </summary>
|
||||
[DataField("maxModules"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public int MaxModules = 3;
|
||||
|
||||
/// <summary>
|
||||
/// The ID for the module container
|
||||
/// </summary>
|
||||
[DataField("moduleContainerId")]
|
||||
public string ModuleContainerId = "borg_module";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Container ModuleContainer = default!;
|
||||
|
||||
public int ModuleCount => ModuleContainer.ContainedEntities.Count;
|
||||
|
||||
/// <summary>
|
||||
/// A list of modules that fill the borg on round start.
|
||||
/// </summary>
|
||||
[DataField("startingModules", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
|
||||
public List<string> StartingModules = new();
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// The job that corresponds to borgs
|
||||
/// </summary>
|
||||
[DataField("borgJobId", customTypeSerializer: typeof(PrototypeIdSerializer<JobPrototype>))]
|
||||
public string BorgJobId = "Borg";
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected module
|
||||
/// </summary>
|
||||
[DataField("selectedModule")]
|
||||
public EntityUid? SelectedModule;
|
||||
|
||||
/// <summary>
|
||||
/// The access this cyborg has when a player is inhabiting it.
|
||||
/// </summary>
|
||||
[DataField("access"), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public string AccessGroup = "AllAccess";
|
||||
|
||||
#region Visuals
|
||||
[DataField("hasMindState")]
|
||||
public string HasMindState = string.Empty;
|
||||
|
||||
[DataField("noMindState")]
|
||||
public string NoMindState = string.Empty;
|
||||
#endregion
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum BorgVisuals : byte
|
||||
{
|
||||
HasPlayer
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum BorgVisualLayers : byte
|
||||
{
|
||||
Light
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Silicons.Borgs.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for modules that can be inserted into borgs
|
||||
/// to give them unique abilities and attributes.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))]
|
||||
public sealed class BorgModuleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity this module is installed into
|
||||
/// </summary>
|
||||
[DataField("installedEntity")]
|
||||
public EntityUid? InstalledEntity;
|
||||
|
||||
public bool Installed => InstalledEntity != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on a module when it is installed in order to add specific behavior to an entity.
|
||||
/// </summary>
|
||||
/// <param name="ChassisEnt"></param>
|
||||
[ByRefEvent]
|
||||
public readonly record struct BorgModuleInstalledEvent(EntityUid ChassisEnt);
|
||||
|
||||
/// <summary>
|
||||
/// Raised on a module when it's uninstalled in order to
|
||||
/// </summary>
|
||||
/// <param name="ChassisEnt"></param>
|
||||
[ByRefEvent]
|
||||
public readonly record struct BorgModuleUninstalledEvent(EntityUid ChassisEnt);
|
||||
@@ -0,0 +1,51 @@
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Shared.Silicons.Borgs.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for a <see cref="BorgModuleComponent"/> that provides items to the entity it's installed into.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))]
|
||||
public sealed class ItemBorgModuleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The items that are provided.
|
||||
/// </summary>
|
||||
[DataField("items", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>), required: true)]
|
||||
public List<string> Items = new();
|
||||
|
||||
/// <summary>
|
||||
/// The entities from <see cref="Items"/> that were spawned.
|
||||
/// </summary>
|
||||
[DataField("providedItems")]
|
||||
public SortedDictionary<string, EntityUid> ProvidedItems = new();
|
||||
|
||||
/// <summary>
|
||||
/// A counter that ensures a unique
|
||||
/// </summary>
|
||||
[DataField("handCounter")]
|
||||
public int HandCounter;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the items have been created and stored in <see cref="ProvidedContainer"/>
|
||||
/// </summary>
|
||||
[DataField("itemsCrated")]
|
||||
public bool ItemsCreated;
|
||||
|
||||
/// <summary>
|
||||
/// A container where provided items are stored when not being used.
|
||||
/// This is helpful as it means that items retain state.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Container ProvidedContainer = default!;
|
||||
|
||||
/// <summary>
|
||||
/// An ID for the container where provided items are stored when not used.
|
||||
/// </summary>
|
||||
[DataField("providedContainerId")]
|
||||
public string ProvidedContainerId = "provided_container";
|
||||
}
|
||||
|
||||
49
Content.Shared/Silicons/Borgs/Components/MMIComponent.cs
Normal file
49
Content.Shared/Silicons/Borgs/Components/MMIComponent.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Silicons.Borgs.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for an entity that takes a brain
|
||||
/// in an item slot before transferring consciousness.
|
||||
/// Used for borg stuff.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))]
|
||||
public sealed class MMIComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the itemslot that holds the brain.
|
||||
/// </summary>
|
||||
[DataField("brainSlotId")]
|
||||
public string BrainSlotId = "brain_slot";
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ItemSlot"/> for this implanter
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public ItemSlot BrainSlot = default!;
|
||||
|
||||
[DataField("hasMindState")]
|
||||
public string HasMindState = "mmi_alive";
|
||||
|
||||
[DataField("noMindState")]
|
||||
public string NoMindState = "mmi_dead";
|
||||
|
||||
[DataField("noBrainState")]
|
||||
public string NoBrainState = "mmi_off";
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum MMIVisuals : byte
|
||||
{
|
||||
BrainPresent,
|
||||
HasMind
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum MMIVisualLayers : byte
|
||||
{
|
||||
Brain,
|
||||
Base
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Silicons.Borgs.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for an entity that is linked to an MMI.
|
||||
/// Mostly for receiving events.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))]
|
||||
public sealed class MMILinkedComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The MMI this entity is linked to.
|
||||
/// </summary>
|
||||
[DataField("linkedMMI")]
|
||||
public EntityUid? LinkedMMI;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Silicons.Borgs.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for <see cref="BorgModuleComponent"/>s that can be "swapped" to, as opposed to having passive effects.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem))]
|
||||
public sealed class SelectableBorgModuleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The sidebar action for swapping to this module.
|
||||
/// </summary>
|
||||
[DataField("moduleSwapAction")]
|
||||
public InstantAction ModuleSwapAction = new()
|
||||
{
|
||||
DisplayName = "action-name-swap-module",
|
||||
Description = "action-desc-swap-module",
|
||||
ItemIconStyle = ItemActionIconStyle.BigItem,
|
||||
Event = new BorgModuleActionSelectedEvent(),
|
||||
UseDelay = TimeSpan.FromSeconds(0.5f)
|
||||
};
|
||||
}
|
||||
|
||||
public sealed class BorgModuleActionSelectedEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised by-ref on a module when it is selected
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct BorgModuleSelectedEvent(EntityUid Chassis);
|
||||
|
||||
/// <summary>
|
||||
/// Event raised by-ref on a module when it is deselected.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct BorgModuleUnselectedEvent(EntityUid Chassis);
|
||||
38
Content.Shared/Silicons/Borgs/SharedBorgSystem.Relay.cs
Normal file
38
Content.Shared/Silicons/Borgs/SharedBorgSystem.Relay.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
|
||||
namespace Content.Shared.Silicons.Borgs;
|
||||
|
||||
public abstract partial class SharedBorgSystem
|
||||
{
|
||||
public void InitializeRelay()
|
||||
{
|
||||
SubscribeLocalEvent<BorgChassisComponent, DamageModifyEvent>(RelayToModule);
|
||||
}
|
||||
|
||||
protected void RelayToModule<T>(EntityUid uid, BorgChassisComponent component, T args) where T : class
|
||||
{
|
||||
var ev = new BorgModuleRelayedEvent<T>(args);
|
||||
|
||||
foreach (var module in component.ModuleContainer.ContainedEntities)
|
||||
{
|
||||
RaiseLocalEvent(module, ref ev);
|
||||
}
|
||||
}
|
||||
|
||||
protected void RelayRefToModule<T>(EntityUid uid, BorgChassisComponent component, ref T args) where T : class
|
||||
{
|
||||
var ev = new BorgModuleRelayedEvent<T>(args);
|
||||
|
||||
foreach (var module in component.ModuleContainer.ContainedEntities)
|
||||
{
|
||||
RaiseLocalEvent(module, ref ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct BorgModuleRelayedEvent<TEvent>(TEvent Args)
|
||||
{
|
||||
public readonly TEvent Args = Args;
|
||||
}
|
||||
107
Content.Shared/Silicons/Borgs/SharedBorgSystem.cs
Normal file
107
Content.Shared/Silicons/Borgs/SharedBorgSystem.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.Wires;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Shared.Silicons.Borgs;
|
||||
|
||||
/// <summary>
|
||||
/// This handles logic, interactions, and UI related to <see cref="BorgChassisComponent"/> and other related components.
|
||||
/// </summary>
|
||||
public abstract partial class SharedBorgSystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly SharedContainerSystem Container = default!;
|
||||
[Dependency] protected readonly ItemSlotsSystem ItemSlots = default!;
|
||||
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BorgChassisComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<BorgChassisComponent, ItemSlotInsertAttemptEvent>(OnItemSlotInsertAttempt);
|
||||
SubscribeLocalEvent<BorgChassisComponent, ItemSlotEjectAttemptEvent>(OnItemSlotEjectAttempt);
|
||||
SubscribeLocalEvent<BorgChassisComponent, EntInsertedIntoContainerMessage>(OnInserted);
|
||||
SubscribeLocalEvent<BorgChassisComponent, EntRemovedFromContainerMessage>(OnRemoved);
|
||||
SubscribeLocalEvent<BorgChassisComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
|
||||
SubscribeLocalEvent<BorgChassisComponent, GetAccessTagsEvent>(OnGetAccessTags);
|
||||
|
||||
InitializeRelay();
|
||||
}
|
||||
|
||||
private void OnItemSlotInsertAttempt(EntityUid uid, BorgChassisComponent component, ref ItemSlotInsertAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (!TryComp<PowerCellSlotComponent>(uid, out var cellSlotComp) ||
|
||||
!TryComp<WiresPanelComponent>(uid, out var panel))
|
||||
return;
|
||||
|
||||
if (!ItemSlots.TryGetSlot(uid, cellSlotComp.CellSlotId, out var cellSlot) || cellSlot != args.Slot)
|
||||
return;
|
||||
|
||||
if (!panel.Open || args.User == uid)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnItemSlotEjectAttempt(EntityUid uid, BorgChassisComponent component, ref ItemSlotEjectAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (!TryComp<PowerCellSlotComponent>(uid, out var cellSlotComp) ||
|
||||
!TryComp<WiresPanelComponent>(uid, out var panel))
|
||||
return;
|
||||
|
||||
if (!ItemSlots.TryGetSlot(uid, cellSlotComp.CellSlotId, out var cellSlot) || cellSlot != args.Slot)
|
||||
return;
|
||||
|
||||
if (!panel.Open || args.User == uid)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, BorgChassisComponent component, ComponentStartup args)
|
||||
{
|
||||
var containerManager = EnsureComp<ContainerManagerComponent>(uid);
|
||||
|
||||
component.BrainContainer = Container.EnsureContainer<ContainerSlot>(uid, component.BrainContainerId, containerManager);
|
||||
component.ModuleContainer = Container.EnsureContainer<Container>(uid, component.ModuleContainerId, containerManager);
|
||||
}
|
||||
|
||||
protected virtual void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void OnRemoved(EntityUid uid, BorgChassisComponent component, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void OnRefreshMovementSpeedModifiers(EntityUid uid, BorgChassisComponent component, RefreshMovementSpeedModifiersEvent args)
|
||||
{
|
||||
if (component.Activated)
|
||||
return;
|
||||
|
||||
if (!TryComp<MovementSpeedModifierComponent>(uid, out var movement))
|
||||
return;
|
||||
|
||||
var sprintDif = movement.BaseWalkSpeed / movement.BaseSprintSpeed;
|
||||
args.ModifySpeed(1f, sprintDif);
|
||||
}
|
||||
|
||||
private void OnGetAccessTags(EntityUid uid, BorgChassisComponent component, ref GetAccessTagsEvent args)
|
||||
{
|
||||
if (!component.HasPlayer)
|
||||
return;
|
||||
args.AddGroup(component.AccessGroup);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Content.Shared.Silicons.Laws.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for an entity that grants a special "obey" law when emagge.d
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class EmagSiliconLawComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the person who emagged this law provider.
|
||||
/// </summary>
|
||||
[DataField("ownerName")]
|
||||
public string? OwnerName;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Silicons.Laws.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for entities which are bound to silicon laws and can view them.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class SiliconLawBoundComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The sidebar action that toggles the laws screen.
|
||||
/// </summary>
|
||||
[DataField("viewLawsAction", customTypeSerializer: typeof(PrototypeIdSerializer<InstantActionPrototype>))]
|
||||
public string ViewLawsAction = "ViewLaws";
|
||||
|
||||
/// <summary>
|
||||
/// The action for toggling laws. Stored here so we can remove it later.
|
||||
/// </summary>
|
||||
[DataField("providedAction")]
|
||||
public InstantAction? ProvidedAction;
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct GetSiliconLawsEvent(EntityUid Entity)
|
||||
{
|
||||
public EntityUid Entity = Entity;
|
||||
|
||||
public readonly List<SiliconLaw> Laws = new();
|
||||
|
||||
public bool Handled = false;
|
||||
}
|
||||
|
||||
public sealed class ToggleLawsScreenEvent : InstantActionEvent
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public enum SiliconLawsUiKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SiliconLawBuiState : BoundUserInterfaceState
|
||||
{
|
||||
public List<SiliconLaw> Laws;
|
||||
|
||||
public SiliconLawBuiState(List<SiliconLaw> laws)
|
||||
{
|
||||
Laws = laws;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Shared.Silicons.Laws.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for an entity which grants laws to a <see cref="SiliconLawBoundComponent"/>
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class SiliconLawProviderComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The laws that are provided.
|
||||
/// </summary>
|
||||
[DataField("laws", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer<SiliconLawPrototype>))]
|
||||
public List<string> Laws = new();
|
||||
}
|
||||
22
Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs
Normal file
22
Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Content.Shared.Silicons.Laws.Components;
|
||||
|
||||
namespace Content.Shared.Silicons.Laws;
|
||||
|
||||
/// <summary>
|
||||
/// This handles getting and displaying the laws for silicons.
|
||||
/// </summary>
|
||||
public abstract class SharedSiliconLawSystem : EntitySystem
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<EmagSiliconLawComponent, GotEmaggedEvent>(OnGotEmagged);
|
||||
}
|
||||
|
||||
protected virtual void OnGotEmagged(EntityUid uid, EmagSiliconLawComponent component, ref GotEmaggedEvent args)
|
||||
{
|
||||
component.OwnerName = Name(args.UserUid);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
55
Content.Shared/Silicons/Laws/SiliconLawPrototype.cs
Normal file
55
Content.Shared/Silicons/Laws/SiliconLawPrototype.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Silicons.Laws;
|
||||
|
||||
[Virtual, DataDefinition]
|
||||
[Serializable, NetSerializable]
|
||||
public class SiliconLaw : IComparable<SiliconLaw>
|
||||
{
|
||||
/// <summary>
|
||||
/// A locale string which is the actual text of the law.
|
||||
/// </summary>
|
||||
[DataField("lawString", required: true)]
|
||||
public string LawString = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The order of the law in the sequence.
|
||||
/// Also is the identifier if <see cref="LawIdentifierOverride"/> is null.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a fixedpoint2 only for the niche case of supporting laws that go between 0 and 1.
|
||||
/// Funny.
|
||||
/// </remarks>
|
||||
[DataField("order", required: true)]
|
||||
public FixedPoint2 Order;
|
||||
|
||||
/// <summary>
|
||||
/// An identifier that overrides <see cref="Order"/> in the law menu UI.
|
||||
/// </summary>
|
||||
[DataField("lawIdentifierOverride")]
|
||||
public string? LawIdentifierOverride;
|
||||
|
||||
public int CompareTo(SiliconLaw? other)
|
||||
{
|
||||
if (other == null)
|
||||
return -1;
|
||||
|
||||
return Order.CompareTo(other.Order);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a prototype for a law governing the behavior of silicons.
|
||||
/// </summary>
|
||||
[Prototype("siliconLaw")]
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SiliconLawPrototype : SiliconLaw, IPrototype
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
|
||||
|
||||
}
|
||||
@@ -34,12 +34,19 @@ namespace Content.Shared.Stacks
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool Unlimited { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lingering stacks will remain present even when there are no items.
|
||||
/// Instead, they will become transparent.
|
||||
/// </summary>
|
||||
[DataField("lingering"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Lingering;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool ThrowIndividually { get; set; } = false;
|
||||
|
||||
[ViewVariables]
|
||||
public bool UiUpdateNeeded { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Default IconLayer stack.
|
||||
/// </summary>
|
||||
|
||||
@@ -395,7 +395,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
|
||||
|
||||
var targetIsMob = HasComp<BodyComponent>(toInsert);
|
||||
var storageIsItem = HasComp<ItemComponent>(container);
|
||||
var allowedToEat = whitelist?.IsValid(toInsert) ?? HasComp<ItemComponent>(toInsert);
|
||||
var allowedToEat = HasComp<ItemComponent>(toInsert);
|
||||
|
||||
// BEFORE REPLACING THIS WITH, I.E. A PROPERTY:
|
||||
// Make absolutely 100% sure you have worked out how to stop people ending up in backpacks.
|
||||
@@ -414,6 +414,9 @@ public abstract class SharedEntityStorageSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
if (allowedToEat && whitelist != null)
|
||||
allowedToEat = whitelist.IsValid(toInsert);
|
||||
|
||||
return allowedToEat;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Content.Shared.Verbs
|
||||
else if (_interactionSystem.InRangeUnobstructed(user, target))
|
||||
{
|
||||
// Note that being in a container does not count as an obstruction for InRangeUnobstructed
|
||||
// Therefore, we need extra checks to ensure the item is actually accessible:
|
||||
// Therefore, we need extra checks to ensure the item is actually accessible:
|
||||
if (ContainerSystem.IsInSameOrParentContainer(user, target))
|
||||
canAccess = true;
|
||||
else
|
||||
@@ -81,15 +81,23 @@ namespace Content.Shared.Verbs
|
||||
EntityUid? @using = null;
|
||||
if (TryComp(user, out HandsComponent? hands) && (force || _actionBlockerSystem.CanUseHeldEntity(user)))
|
||||
{
|
||||
@using = hands.ActiveHandEntity;
|
||||
|
||||
// Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used".
|
||||
// This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging
|
||||
// their sprite.
|
||||
|
||||
if (TryComp(@using, out HandVirtualItemComponent? pull))
|
||||
// if we don't actually have any hands, pass in a null value for the events.
|
||||
if (hands.Count == 0)
|
||||
{
|
||||
@using = pull.BlockingEntity;
|
||||
hands = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@using = hands.ActiveHandEntity;
|
||||
|
||||
// Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used".
|
||||
// This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging
|
||||
// their sprite.
|
||||
|
||||
if (TryComp(@using, out HandVirtualItemComponent? pull))
|
||||
{
|
||||
@using = pull.BlockingEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user