* 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:
Nemanja
2023-08-12 17:39:58 -04:00
committed by GitHub
parent ac4f496535
commit 98fa00a21f
314 changed files with 7094 additions and 484 deletions

View 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;
}
}

View File

@@ -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
{
}

View 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
}

View File

@@ -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);

View File

@@ -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";
}

View 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
}

View File

@@ -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;
}

View File

@@ -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);

View 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;
}

View 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);
}
}