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:
79
Content.Server/Silicons/Borgs/BorgSystem.MMI.cs
Normal file
79
Content.Server/Silicons/Borgs/BorgSystem.MMI.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.Silicons.Borgs;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed partial class BorgSystem
|
||||
{
|
||||
public void InitializeMMI()
|
||||
{
|
||||
SubscribeLocalEvent<MMIComponent, ComponentInit>(OnMMIInit);
|
||||
SubscribeLocalEvent<MMIComponent, EntInsertedIntoContainerMessage>(OnMMIEntityInserted);
|
||||
SubscribeLocalEvent<MMIComponent, MindAddedMessage>(OnMMIMindAdded);
|
||||
SubscribeLocalEvent<MMIComponent, MindRemovedMessage>(OnMMIMindRemoved);
|
||||
|
||||
SubscribeLocalEvent<MMILinkedComponent, MindAddedMessage>(OnMMILinkedMindAdded);
|
||||
SubscribeLocalEvent<MMILinkedComponent, EntGotRemovedFromContainerMessage>(OnMMILinkedRemoved);
|
||||
}
|
||||
|
||||
private void OnMMIInit(EntityUid uid, MMIComponent component, ComponentInit args)
|
||||
{
|
||||
if (!TryComp<ItemSlotsComponent>(uid, out var itemSlots))
|
||||
return;
|
||||
|
||||
if (ItemSlots.TryGetSlot(uid, component.BrainSlotId, out var slot, itemSlots))
|
||||
component.BrainSlot = slot;
|
||||
else
|
||||
ItemSlots.AddItemSlot(uid, component.BrainSlotId, component.BrainSlot, itemSlots);
|
||||
}
|
||||
|
||||
private void OnMMIEntityInserted(EntityUid uid, MMIComponent component, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != component.BrainSlotId)
|
||||
return;
|
||||
|
||||
var ent = args.Entity;
|
||||
var linked = EnsureComp<MMILinkedComponent>(ent);
|
||||
linked.LinkedMMI = uid;
|
||||
|
||||
if (_mind.TryGetMind(ent, out var mind))
|
||||
_mind.TransferTo(mind, uid, true);
|
||||
|
||||
_appearance.SetData(uid, MMIVisuals.BrainPresent, true);
|
||||
}
|
||||
|
||||
private void OnMMIMindAdded(EntityUid uid, MMIComponent component, MindAddedMessage args)
|
||||
{
|
||||
_appearance.SetData(uid, MMIVisuals.HasMind, true);
|
||||
}
|
||||
|
||||
private void OnMMIMindRemoved(EntityUid uid, MMIComponent component, MindRemovedMessage args)
|
||||
{
|
||||
_appearance.SetData(uid, MMIVisuals.HasMind, false);
|
||||
}
|
||||
|
||||
private void OnMMILinkedMindAdded(EntityUid uid, MMILinkedComponent component, MindAddedMessage args)
|
||||
{
|
||||
if (!_mind.TryGetMind(uid, out var mind) || component.LinkedMMI == null)
|
||||
return;
|
||||
_mind.TransferTo(mind, component.LinkedMMI, true);
|
||||
}
|
||||
|
||||
private void OnMMILinkedRemoved(EntityUid uid, MMILinkedComponent component, EntGotRemovedFromContainerMessage args)
|
||||
{
|
||||
if (Terminating(uid))
|
||||
return;
|
||||
|
||||
if (component.LinkedMMI is not { } linked)
|
||||
return;
|
||||
RemComp(uid, component);
|
||||
|
||||
if (_mind.TryGetMind(linked, out var mind))
|
||||
_mind.TransferTo(mind, uid, true);
|
||||
|
||||
_appearance.SetData(linked, MMIVisuals.BrainPresent, false);
|
||||
}
|
||||
}
|
||||
316
Content.Server/Silicons/Borgs/BorgSystem.Modules.cs
Normal file
316
Content.Server/Silicons/Borgs/BorgSystem.Modules.cs
Normal file
@@ -0,0 +1,316 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.Silicons.Borgs;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed partial class BorgSystem
|
||||
{
|
||||
public void InitializeModules()
|
||||
{
|
||||
SubscribeLocalEvent<BorgModuleComponent, EntGotInsertedIntoContainerMessage>(OnModuleGotInserted);
|
||||
SubscribeLocalEvent<BorgModuleComponent, EntGotRemovedFromContainerMessage>(OnModuleGotRemoved);
|
||||
|
||||
SubscribeLocalEvent<SelectableBorgModuleComponent, BorgModuleInstalledEvent>(OnSelectableInstalled);
|
||||
SubscribeLocalEvent<SelectableBorgModuleComponent, BorgModuleUninstalledEvent>(OnSelectableUninstalled);
|
||||
SubscribeLocalEvent<SelectableBorgModuleComponent, BorgModuleActionSelectedEvent>(OnSelectableAction);
|
||||
|
||||
SubscribeLocalEvent<ItemBorgModuleComponent, ComponentStartup>(OnProvideItemStartup);
|
||||
SubscribeLocalEvent<ItemBorgModuleComponent, BorgModuleSelectedEvent>(OnItemModuleSelected);
|
||||
SubscribeLocalEvent<ItemBorgModuleComponent, BorgModuleUnselectedEvent>(OnItemModuleUnselected);
|
||||
}
|
||||
|
||||
private void OnModuleGotInserted(EntityUid uid, BorgModuleComponent component, EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
var chassis = args.Container.Owner;
|
||||
|
||||
if (!TryComp<BorgChassisComponent>(chassis, out var chassisComp) ||
|
||||
args.Container != chassisComp.ModuleContainer ||
|
||||
!chassisComp.Activated)
|
||||
return;
|
||||
|
||||
if (!_powerCell.HasDrawCharge(uid))
|
||||
return;
|
||||
|
||||
InstallModule(chassis, uid, chassisComp, component);
|
||||
}
|
||||
|
||||
private void OnModuleGotRemoved(EntityUid uid, BorgModuleComponent component, EntGotRemovedFromContainerMessage args)
|
||||
{
|
||||
var chassis = args.Container.Owner;
|
||||
|
||||
if (Terminating(chassis))
|
||||
return;
|
||||
|
||||
if (!TryComp<BorgChassisComponent>(chassis, out var chassisComp) ||
|
||||
args.Container != chassisComp.ModuleContainer)
|
||||
return;
|
||||
|
||||
UninstallModule(chassis, uid, chassisComp, component);
|
||||
}
|
||||
|
||||
private void OnProvideItemStartup(EntityUid uid, ItemBorgModuleComponent component, ComponentStartup args)
|
||||
{
|
||||
component.ProvidedContainer = Container.EnsureContainer<Container>(uid, component.ProvidedContainerId);
|
||||
}
|
||||
|
||||
private void OnSelectableInstalled(EntityUid uid, SelectableBorgModuleComponent component, ref BorgModuleInstalledEvent args)
|
||||
{
|
||||
var chassis = args.ChassisEnt;
|
||||
component.ModuleSwapAction.EntityIcon = uid;
|
||||
_actions.AddAction(chassis, component.ModuleSwapAction, uid);
|
||||
SelectModule(chassis, uid, moduleComp: component);
|
||||
}
|
||||
|
||||
private void OnSelectableUninstalled(EntityUid uid, SelectableBorgModuleComponent component, ref BorgModuleUninstalledEvent args)
|
||||
{
|
||||
var chassis = args.ChassisEnt;
|
||||
_actions.RemoveProvidedActions(chassis, uid);
|
||||
UnselectModule(chassis, uid, moduleComp: component);
|
||||
}
|
||||
|
||||
private void OnSelectableAction(EntityUid uid, SelectableBorgModuleComponent component, BorgModuleActionSelectedEvent args)
|
||||
{
|
||||
var chassis = args.Performer;
|
||||
if (!TryComp<BorgChassisComponent>(chassis, out var chassisComp))
|
||||
return;
|
||||
|
||||
if (chassisComp.SelectedModule == uid)
|
||||
{
|
||||
UnselectModule(chassis, chassisComp.SelectedModule, chassisComp);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
UnselectModule(chassis, chassisComp.SelectedModule, chassisComp);
|
||||
SelectModule(chassis, uid, chassisComp, component);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects a module, enablind the borg to use its provided abilities.
|
||||
/// </summary>
|
||||
public void SelectModule(EntityUid chassis,
|
||||
EntityUid moduleUid,
|
||||
BorgChassisComponent? chassisComp = null,
|
||||
SelectableBorgModuleComponent? moduleComp = null)
|
||||
{
|
||||
if (Terminating(chassis) || Deleted(chassis))
|
||||
return;
|
||||
|
||||
if (!Resolve(chassis, ref chassisComp))
|
||||
return;
|
||||
|
||||
if (chassisComp.SelectedModule != null)
|
||||
return;
|
||||
|
||||
if (chassisComp.SelectedModule == moduleUid)
|
||||
return;
|
||||
|
||||
if (!Resolve(moduleUid, ref moduleComp, false))
|
||||
return;
|
||||
|
||||
var ev = new BorgModuleSelectedEvent(chassis);
|
||||
RaiseLocalEvent(moduleUid, ref ev);
|
||||
chassisComp.SelectedModule = moduleUid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unselects a module, removing its provided abilities
|
||||
/// </summary>
|
||||
public void UnselectModule(EntityUid chassis,
|
||||
EntityUid? moduleUid,
|
||||
BorgChassisComponent? chassisComp = null,
|
||||
SelectableBorgModuleComponent? moduleComp = null)
|
||||
{
|
||||
if (Terminating(chassis) || Deleted(chassis))
|
||||
return;
|
||||
|
||||
if (!Resolve(chassis, ref chassisComp))
|
||||
return;
|
||||
|
||||
if (moduleUid == null)
|
||||
return;
|
||||
|
||||
if (chassisComp.SelectedModule != moduleUid)
|
||||
return;
|
||||
|
||||
if (!Resolve(moduleUid.Value, ref moduleComp, false))
|
||||
return;
|
||||
|
||||
var ev = new BorgModuleUnselectedEvent(chassis);
|
||||
RaiseLocalEvent(moduleUid.Value, ref ev);
|
||||
chassisComp.SelectedModule = null;
|
||||
}
|
||||
|
||||
private void OnItemModuleSelected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleSelectedEvent args)
|
||||
{
|
||||
ProvideItems(args.Chassis, uid, component: component);
|
||||
}
|
||||
|
||||
private void OnItemModuleUnselected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleUnselectedEvent args)
|
||||
{
|
||||
RemoveProvidedItems(args.Chassis, uid, component: component);
|
||||
}
|
||||
|
||||
private void ProvideItems(EntityUid chassis, EntityUid uid, BorgChassisComponent? chassisComponent = null, ItemBorgModuleComponent? component = null)
|
||||
{
|
||||
if (!Resolve(chassis, ref chassisComponent) || !Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (!TryComp<HandsComponent>(chassis, out var hands))
|
||||
return;
|
||||
|
||||
var xform = Transform(chassis);
|
||||
foreach (var itemProto in component.Items)
|
||||
{
|
||||
EntityUid item;
|
||||
|
||||
if (!component.ItemsCreated)
|
||||
{
|
||||
item = Spawn(itemProto, xform.Coordinates);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = component.ProvidedContainer.ContainedEntities
|
||||
.FirstOrDefault(ent => Prototype(ent)?.ID == itemProto);
|
||||
if (!item.IsValid())
|
||||
{
|
||||
Log.Debug($"no items found: {component.ProvidedContainer.ContainedEntities.Count}");
|
||||
continue;
|
||||
}
|
||||
|
||||
component.ProvidedContainer.Remove(item, EntityManager, force: true);
|
||||
}
|
||||
|
||||
if (!item.IsValid())
|
||||
{
|
||||
Log.Debug("no valid item");
|
||||
continue;
|
||||
}
|
||||
|
||||
var handId = $"{uid}-item{component.HandCounter}";
|
||||
component.HandCounter++;
|
||||
_hands.AddHand(chassis, handId, HandLocation.Middle, hands);
|
||||
_hands.DoPickup(chassis, hands.Hands[handId], item, hands);
|
||||
EnsureComp<UnremoveableComponent>(item);
|
||||
component.ProvidedItems.Add(handId, item);
|
||||
}
|
||||
|
||||
component.ItemsCreated = true;
|
||||
}
|
||||
|
||||
private void RemoveProvidedItems(EntityUid chassis, EntityUid uid, BorgChassisComponent? chassisComponent = null, ItemBorgModuleComponent? component = null)
|
||||
{
|
||||
if (!Resolve(chassis, ref chassisComponent) || !Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (!TryComp<HandsComponent>(chassis, out var hands))
|
||||
return;
|
||||
|
||||
foreach (var (handId, item) in component.ProvidedItems)
|
||||
{
|
||||
if (!Deleted(item) && !Terminating(item))
|
||||
{
|
||||
RemComp<UnremoveableComponent>(item);
|
||||
component.ProvidedContainer.Insert(item, EntityManager);
|
||||
}
|
||||
_hands.RemoveHand(chassis, handId, hands);
|
||||
}
|
||||
component.ProvidedItems.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given module can be inserted into a borg
|
||||
/// </summary>
|
||||
public bool CanInsertModule(EntityUid uid, EntityUid module, BorgChassisComponent? component = null, BorgModuleComponent? moduleComponent = null, EntityUid? user = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent))
|
||||
return false;
|
||||
|
||||
if (component.ModuleContainer.ContainedEntities.Count >= component.MaxModules)
|
||||
return false;
|
||||
|
||||
if (component.ModuleWhitelist?.IsValid(module, EntityManager) == false)
|
||||
{
|
||||
if (user != null)
|
||||
Popup.PopupEntity(Loc.GetString("borg-module-whitelist-deny"), uid, user.Value);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs and activates all modules currently inside the borg's module container
|
||||
/// </summary>
|
||||
public void InstallAllModules(EntityUid uid, BorgChassisComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
var query = GetEntityQuery<BorgModuleComponent>();
|
||||
foreach (var moduleEnt in new List<EntityUid>(component.ModuleContainer.ContainedEntities))
|
||||
{
|
||||
if (!query.TryGetComponent(moduleEnt, out var moduleComp))
|
||||
continue;
|
||||
|
||||
InstallModule(uid, moduleEnt, component, moduleComp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates all modules currently inside the borg's module container
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="component"></param>
|
||||
public void DisableAllModules(EntityUid uid, BorgChassisComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
var query = GetEntityQuery<BorgModuleComponent>();
|
||||
foreach (var moduleEnt in new List<EntityUid>(component.ModuleContainer.ContainedEntities))
|
||||
{
|
||||
if (!query.TryGetComponent(moduleEnt, out var moduleComp))
|
||||
continue;
|
||||
|
||||
UninstallModule(uid, moduleEnt, component, moduleComp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs a single module into a borg.
|
||||
/// </summary>
|
||||
public void InstallModule(EntityUid uid, EntityUid module, BorgChassisComponent? component, BorgModuleComponent? moduleComponent = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent))
|
||||
return;
|
||||
|
||||
if (moduleComponent.Installed)
|
||||
return;
|
||||
|
||||
moduleComponent.InstalledEntity = uid;
|
||||
var ev = new BorgModuleInstalledEvent(uid);
|
||||
RaiseLocalEvent(module, ref ev);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uninstalls a single module from a borg.
|
||||
/// </summary>
|
||||
public void UninstallModule(EntityUid uid, EntityUid module, BorgChassisComponent? component, BorgModuleComponent? moduleComponent = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent))
|
||||
return;
|
||||
|
||||
if (!moduleComponent.Installed)
|
||||
return;
|
||||
|
||||
moduleComponent.InstalledEntity = null;
|
||||
var ev = new BorgModuleUninstalledEvent(uid);
|
||||
RaiseLocalEvent(module, ref ev);
|
||||
}
|
||||
}
|
||||
108
Content.Server/Silicons/Borgs/BorgSystem.Ui.cs
Normal file
108
Content.Server/Silicons/Borgs/BorgSystem.Ui.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System.Linq;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.NameIdentifier;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
|
||||
namespace Content.Server.Silicons.Borgs;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed partial class BorgSystem
|
||||
{
|
||||
public void InitializeUI()
|
||||
{
|
||||
SubscribeLocalEvent<BorgChassisComponent, BeforeActivatableUIOpenEvent>(OnBeforeBorgUiOpen);
|
||||
SubscribeLocalEvent<BorgChassisComponent, BorgEjectBrainBuiMessage>(OnEjectBrainBuiMessage);
|
||||
SubscribeLocalEvent<BorgChassisComponent, BorgEjectBatteryBuiMessage>(OnEjectBatteryBuiMessage);
|
||||
SubscribeLocalEvent<BorgChassisComponent, BorgSetNameBuiMessage>(OnSetNameBuiMessage);
|
||||
SubscribeLocalEvent<BorgChassisComponent, BorgRemoveModuleBuiMessage>(OnRemoveModuleBuiMessage);
|
||||
}
|
||||
|
||||
private void OnBeforeBorgUiOpen(EntityUid uid, BorgChassisComponent component, BeforeActivatableUIOpenEvent args)
|
||||
{
|
||||
UpdateUI(uid, component);
|
||||
}
|
||||
|
||||
private void OnEjectBrainBuiMessage(EntityUid uid, BorgChassisComponent component, BorgEjectBrainBuiMessage args)
|
||||
{
|
||||
if (args.Session.AttachedEntity is not { } attachedEntity || component.BrainEntity is not { } brain)
|
||||
return;
|
||||
|
||||
_adminLog.Add(LogType.Action, LogImpact.Medium,
|
||||
$"{ToPrettyString(attachedEntity):player} removed brain {ToPrettyString(brain)} from borg {ToPrettyString(uid)}");
|
||||
component.BrainContainer.Remove(brain, EntityManager);
|
||||
_hands.TryPickupAnyHand(attachedEntity, brain);
|
||||
UpdateUI(uid, component);
|
||||
}
|
||||
|
||||
private void OnEjectBatteryBuiMessage(EntityUid uid, BorgChassisComponent component, BorgEjectBatteryBuiMessage args)
|
||||
{
|
||||
if (args.Session.AttachedEntity is not { } attachedEntity ||
|
||||
!TryComp<PowerCellSlotComponent>(uid, out var slotComp) ||
|
||||
!Container.TryGetContainer(uid, slotComp.CellSlotId, out var container) ||
|
||||
!container.ContainedEntities.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var ents = Container.EmptyContainer(container);
|
||||
_hands.TryPickupAnyHand(attachedEntity, ents.First());
|
||||
}
|
||||
|
||||
private void OnSetNameBuiMessage(EntityUid uid, BorgChassisComponent component, BorgSetNameBuiMessage args)
|
||||
{
|
||||
if (args.Session.AttachedEntity is not { } attachedEntity)
|
||||
return;
|
||||
|
||||
if (args.Name.Length > HumanoidCharacterProfile.MaxNameLength ||
|
||||
args.Name.Length == 0 ||
|
||||
string.IsNullOrWhiteSpace(args.Name) ||
|
||||
string.IsNullOrEmpty(args.Name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var name = args.Name.Trim();
|
||||
if (TryComp<NameIdentifierComponent>(uid, out var identifier))
|
||||
name = $"{name} {identifier.FullIdentifier}";
|
||||
|
||||
_metaData.SetEntityName(uid, name);
|
||||
_adminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(attachedEntity):player} set borg \"{ToPrettyString(uid)}\"'s name to: {name}");
|
||||
}
|
||||
|
||||
private void OnRemoveModuleBuiMessage(EntityUid uid, BorgChassisComponent component, BorgRemoveModuleBuiMessage args)
|
||||
{
|
||||
if (args.Session.AttachedEntity is not { } attachedEntity)
|
||||
return;
|
||||
|
||||
if (!component.ModuleContainer.Contains(args.Module))
|
||||
return;
|
||||
|
||||
_adminLog.Add(LogType.Action, LogImpact.Medium,
|
||||
$"{ToPrettyString(attachedEntity):player} removed module {ToPrettyString(args.Module)} from borg {ToPrettyString(uid)}");
|
||||
component.ModuleContainer.Remove(args.Module);
|
||||
_hands.TryPickupAnyHand(attachedEntity, args.Module);
|
||||
|
||||
UpdateUI(uid, component);
|
||||
}
|
||||
|
||||
public void UpdateUI(EntityUid uid, BorgChassisComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
var chargePercent = 0f;
|
||||
var hasBattery = false;
|
||||
if (_powerCell.TryGetBatteryFromSlot(uid, out var battery))
|
||||
{
|
||||
hasBattery = true;
|
||||
chargePercent = battery.Charge / battery.MaxCharge;
|
||||
}
|
||||
|
||||
var state = new BorgBuiState(chargePercent, hasBattery);
|
||||
_ui.TrySetUiState(uid, BorgUiKey.Key, state);
|
||||
}
|
||||
}
|
||||
318
Content.Server/Silicons/Borgs/BorgSystem.cs
Normal file
318
Content.Server/Silicons/Borgs/BorgSystem.cs
Normal file
@@ -0,0 +1,318 @@
|
||||
using Content.Server.Actions;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Server.Players.PlayTimeTracking;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.PowerCell;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Wires;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Silicons.Borgs;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed partial class BorgSystem : SharedBorgSystem
|
||||
{
|
||||
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
||||
[Dependency] private readonly IBanManager _banManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ActionsSystem _actions = default!;
|
||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly HandsSystem _hands = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
||||
[Dependency] private readonly PlayTimeTrackingSystem _playTimeTracking = default!;
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwing = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BorgChassisComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<BorgChassisComponent, AfterInteractUsingEvent>(OnChassisInteractUsing);
|
||||
SubscribeLocalEvent<BorgChassisComponent, MindAddedMessage>(OnMindAdded);
|
||||
SubscribeLocalEvent<BorgChassisComponent, MindRemovedMessage>(OnMindRemoved);
|
||||
SubscribeLocalEvent<BorgChassisComponent, PowerCellChangedEvent>(OnPowerCellChanged);
|
||||
SubscribeLocalEvent<BorgChassisComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
||||
SubscribeLocalEvent<BorgChassisComponent, ActivatableUIOpenAttemptEvent>(OnUIOpenAttempt);
|
||||
|
||||
SubscribeLocalEvent<BorgBrainComponent, MindAddedMessage>(OnBrainMindAdded);
|
||||
|
||||
InitializeModules();
|
||||
InitializeMMI();
|
||||
InitializeUI();
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, BorgChassisComponent component, MapInitEvent args)
|
||||
{
|
||||
UpdateBatteryAlert(uid);
|
||||
_movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
|
||||
|
||||
var coordinates = Transform(uid).Coordinates;
|
||||
|
||||
if (component.StartingBrain != null)
|
||||
{
|
||||
component.BrainContainer.Insert(Spawn(component.StartingBrain, coordinates), EntityManager);
|
||||
}
|
||||
|
||||
foreach (var startingModule in component.StartingModules)
|
||||
{
|
||||
component.ModuleContainer.Insert(Spawn(startingModule, coordinates), EntityManager);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChassisInteractUsing(EntityUid uid, BorgChassisComponent component, AfterInteractUsingEvent args)
|
||||
{
|
||||
if (!args.CanReach || args.Handled || uid == args.User)
|
||||
return;
|
||||
|
||||
var used = args.Used;
|
||||
TryComp<BorgBrainComponent>(used, out var brain);
|
||||
TryComp<BorgModuleComponent>(used, out var module);
|
||||
|
||||
if (TryComp<WiresPanelComponent>(uid, out var panel) && !panel.Open)
|
||||
{
|
||||
if (brain != null || module != null)
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("borg-panel-not-open"), uid, args.User);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.BrainEntity == null &&
|
||||
brain != null &&
|
||||
component.BrainWhitelist?.IsValid(used) != false)
|
||||
{
|
||||
if (_mind.TryGetMind(used, out var mind) && mind.Session != null)
|
||||
{
|
||||
if (!CanPlayerBeBorgged(mind.Session, component))
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("borg-player-not-allowed"), used, args.User);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
component.BrainContainer.Insert(used);
|
||||
_adminLog.Add(LogType.Action, LogImpact.Medium,
|
||||
$"{ToPrettyString(args.User):player} installed brain {ToPrettyString(used)} into borg {ToPrettyString(uid)}");
|
||||
args.Handled = true;
|
||||
UpdateUI(uid, component);
|
||||
}
|
||||
|
||||
if (module != null && CanInsertModule(uid, used, component, module, args.User))
|
||||
{
|
||||
component.ModuleContainer.Insert(used);
|
||||
_adminLog.Add(LogType.Action, LogImpact.Low,
|
||||
$"{ToPrettyString(args.User):player} installed module {ToPrettyString(used)} into borg {ToPrettyString(uid)}");
|
||||
args.Handled = true;
|
||||
UpdateUI(uid, component);
|
||||
}
|
||||
}
|
||||
|
||||
// todo: consider transferring over the ghost role? managing that might suck.
|
||||
protected override void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
base.OnInserted(uid, component, args);
|
||||
|
||||
if (HasComp<BorgBrainComponent>(args.Entity) && _mind.TryGetMind(args.Entity, out var mind))
|
||||
{
|
||||
_mind.TransferTo(mind, uid);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnRemoved(EntityUid uid, BorgChassisComponent component, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
base.OnRemoved(uid, component, args);
|
||||
|
||||
if (HasComp<BorgBrainComponent>(args.Entity) && _mind.TryGetMind(uid, out var mind))
|
||||
{
|
||||
_mind.TransferTo(mind, args.Entity);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMindAdded(EntityUid uid, BorgChassisComponent component, MindAddedMessage args)
|
||||
{
|
||||
BorgActivate(uid, component);
|
||||
}
|
||||
|
||||
private void OnMindRemoved(EntityUid uid, BorgChassisComponent component, MindRemovedMessage args)
|
||||
{
|
||||
BorgDeactivate(uid, component);
|
||||
}
|
||||
|
||||
private void OnPowerCellChanged(EntityUid uid, BorgChassisComponent component, PowerCellChangedEvent args)
|
||||
{
|
||||
UpdateBatteryAlert(uid);
|
||||
|
||||
if (!TryComp<PowerCellDrawComponent>(uid, out var draw))
|
||||
return;
|
||||
|
||||
// if we eject the battery or run out of charge, then disable
|
||||
if (args.Ejected || !_powerCell.HasDrawCharge(uid))
|
||||
{
|
||||
DisableBorgAbilities(uid, component);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we aren't drawing and suddenly get enough power to draw again, reeanble.
|
||||
if (_powerCell.HasDrawCharge(uid, draw))
|
||||
{
|
||||
// only reenable the powerdraw if a player has the role.
|
||||
if (!draw.Drawing && _mind.TryGetMind(uid, out _))
|
||||
_powerCell.SetPowerCellDrawEnabled(uid, true);
|
||||
|
||||
EnableBorgAbilities(uid, component);
|
||||
}
|
||||
|
||||
UpdateUI(uid, component);
|
||||
}
|
||||
|
||||
private void OnPowerCellSlotEmpty(EntityUid uid, BorgChassisComponent component, ref PowerCellSlotEmptyEvent args)
|
||||
{
|
||||
DisableBorgAbilities(uid, component);
|
||||
UpdateUI(uid, component);
|
||||
}
|
||||
|
||||
private void OnUIOpenAttempt(EntityUid uid, BorgChassisComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
// borgs can't view their own ui
|
||||
if (args.User == uid)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnBrainMindAdded(EntityUid uid, BorgBrainComponent component, MindAddedMessage args)
|
||||
{
|
||||
if (!Container.TryGetOuterContainer(uid, Transform(uid), out var container))
|
||||
return;
|
||||
|
||||
var containerEnt = container.Owner;
|
||||
|
||||
if (!TryComp<BorgChassisComponent>(containerEnt, out var chassisComponent) ||
|
||||
container.ID != chassisComponent.BrainContainerId)
|
||||
return;
|
||||
|
||||
if (!_mind.TryGetMind(uid, out var mind) || mind.Session == null)
|
||||
return;
|
||||
|
||||
if (!CanPlayerBeBorgged(mind.Session, chassisComponent))
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("borg-player-not-allowed-eject"), uid);
|
||||
Container.RemoveEntity(containerEnt, uid);
|
||||
_throwing.TryThrow(uid, _random.NextVector2() * 5, 5f);
|
||||
return;
|
||||
}
|
||||
|
||||
_mind.TransferTo(mind, containerEnt);
|
||||
}
|
||||
|
||||
private void UpdateBatteryAlert(EntityUid uid, PowerCellSlotComponent? slotComponent = null)
|
||||
{
|
||||
if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery, slotComponent))
|
||||
{
|
||||
_alerts.ClearAlert(uid, AlertType.BorgBattery);
|
||||
_alerts.ShowAlert(uid, AlertType.BorgBatteryNone);
|
||||
return;
|
||||
}
|
||||
|
||||
var chargePercent = (short) MathF.Round(battery.CurrentCharge / battery.MaxCharge * 10f);
|
||||
|
||||
// we make sure 0 only shows if they have absolutely no battery.
|
||||
// also account for floating point imprecision
|
||||
if (chargePercent == 0 && _powerCell.HasDrawCharge(uid, cell: slotComponent))
|
||||
{
|
||||
chargePercent = 1;
|
||||
}
|
||||
|
||||
_alerts.ClearAlert(uid, AlertType.BorgBatteryNone);
|
||||
_alerts.ShowAlert(uid, AlertType.BorgBattery, chargePercent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates the borg, enabling all of its modules.
|
||||
/// </summary>
|
||||
public void EnableBorgAbilities(EntityUid uid, BorgChassisComponent component)
|
||||
{
|
||||
if (component.Activated)
|
||||
return;
|
||||
|
||||
component.Activated = true;
|
||||
InstallAllModules(uid, component);
|
||||
Dirty(component);
|
||||
_movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates the borg, disabling all of its modules and decreasing its speed.
|
||||
/// </summary>
|
||||
public void DisableBorgAbilities(EntityUid uid, BorgChassisComponent component)
|
||||
{
|
||||
if (!component.Activated)
|
||||
return;
|
||||
|
||||
component.Activated = false;
|
||||
DisableAllModules(uid, component);
|
||||
Dirty(component);
|
||||
_movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates a borg when a player occupies it
|
||||
/// </summary>
|
||||
public void BorgActivate(EntityUid uid, BorgChassisComponent component)
|
||||
{
|
||||
component.HasPlayer = true;
|
||||
Popup.PopupEntity(Loc.GetString("borg-mind-added", ("name", Identity.Name(uid, EntityManager))), uid);
|
||||
_powerCell.SetPowerCellDrawEnabled(uid, true);
|
||||
_appearance.SetData(uid, BorgVisuals.HasPlayer, true);
|
||||
Dirty(uid, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates a borg when a player leaves it.
|
||||
/// </summary>
|
||||
public void BorgDeactivate(EntityUid uid, BorgChassisComponent component)
|
||||
{
|
||||
component.HasPlayer = false;
|
||||
Popup.PopupEntity(Loc.GetString("borg-mind-removed", ("name", Identity.Name(uid, EntityManager))), uid);
|
||||
_powerCell.SetPowerCellDrawEnabled(uid, false);
|
||||
_appearance.SetData(uid, BorgVisuals.HasPlayer, false);
|
||||
Dirty(uid, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that a player has fulfilled the requirements for the borg job.
|
||||
/// If they don't have enough hours, they cannot be placed into a chassis.
|
||||
/// </summary>
|
||||
public bool CanPlayerBeBorgged(IPlayerSession session, BorgChassisComponent component)
|
||||
{
|
||||
var disallowedJobs = _playTimeTracking.GetDisallowedJobs(session);
|
||||
|
||||
if (disallowedJobs.Contains(component.BorgJobId))
|
||||
return false;
|
||||
|
||||
if (_banManager.GetJobBans(session.UserId)?.Contains(component.BorgJobId) == true)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user