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:
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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user