malf killer 9000 (robotics console) (#24855)
* create devicenet frequencies * create borg transponder and give it to all nt borgs * add robotics console * actually implement battery charge display + some fix * tab * real explosion * little safer * disable destroy button clientside too when on cooldown * m * how do i do this when i review things... Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * webedit ops Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> * ui updates * oracle java * do a thing * update ui when a borg times out * maybe fix test * add IsLocked to LockSystem * make destroying gib the chassis again, so emagging isnt sus * use locking * require using alt click to unlock so normal click is open ui * the * use LogType.Action * take this L * pocket lint? * sharer * pro ops * robor pushmarkup * m * update and make it not use prototype anymore * frame0 * update yaml * untroll * bad * h --------- Co-authored-by: deltanedas <@deltanedas:kde.org> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
This commit is contained in:
7
Content.Client/Robotics/Systems/RoboticsConsoleSystem.cs
Normal file
7
Content.Client/Robotics/Systems/RoboticsConsoleSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
using Content.Shared.Robotics.Systems;
|
||||||
|
|
||||||
|
namespace Content.Client.Robotics.Systems;
|
||||||
|
|
||||||
|
public sealed class RoboticsConsoleSystem : SharedRoboticsConsoleSystem
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using Content.Shared.Robotics;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.Robotics.UI;
|
||||||
|
|
||||||
|
public sealed class RoboticsConsoleBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
public RoboticsConsoleWindow _window = default!;
|
||||||
|
|
||||||
|
public RoboticsConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
|
_window = new RoboticsConsoleWindow(Owner);
|
||||||
|
_window.OnDisablePressed += address =>
|
||||||
|
{
|
||||||
|
SendMessage(new RoboticsConsoleDisableMessage(address));
|
||||||
|
};
|
||||||
|
_window.OnDestroyPressed += address =>
|
||||||
|
{
|
||||||
|
SendMessage(new RoboticsConsoleDestroyMessage(address));
|
||||||
|
};
|
||||||
|
_window.OnClose += Close;
|
||||||
|
|
||||||
|
_window.OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
|
||||||
|
if (state is not RoboticsConsoleState cast)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_window?.UpdateState(cast);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
_window?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
40
Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml
Normal file
40
Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||||
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
Title="{Loc 'robotics-console-window-title'}"
|
||||||
|
MinSize="600 450">
|
||||||
|
<BoxContainer Orientation="Vertical">
|
||||||
|
<!-- List of borgs -->
|
||||||
|
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="10 10 10 10">
|
||||||
|
<Label Name="NoCyborgs" Text="{Loc 'robotics-console-no-cyborgs'}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
<ScrollContainer Name="CyborgsContainer" VerticalExpand="True" Visible="False">
|
||||||
|
<!-- Populated when loading state -->
|
||||||
|
<ItemList Name="Cyborgs"/>
|
||||||
|
</ScrollContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
|
||||||
|
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5"/>
|
||||||
|
|
||||||
|
<!-- Selected borg info -->
|
||||||
|
<Label Name="SelectCyborg" Text="{Loc 'robotics-console-select-cyborg'}" HorizontalAlignment="Center"/>
|
||||||
|
<BoxContainer Name="BorgContainer" Orientation="Vertical" MaxHeight="200" Visible="False">
|
||||||
|
<BoxContainer Margin="5 5 5 5" Orientation="Horizontal">
|
||||||
|
<PanelContainer VerticalExpand="True">
|
||||||
|
<BoxContainer HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<TextureRect Name="BorgSprite" TextureScale="4 4"/>
|
||||||
|
</BoxContainer>
|
||||||
|
</PanelContainer>
|
||||||
|
<PanelContainer VerticalExpand="True" HorizontalExpand="True">
|
||||||
|
<RichTextLabel Name="BorgInfo"/>
|
||||||
|
</PanelContainer>
|
||||||
|
<!-- TODO: button to open camera window for this borg -->
|
||||||
|
</BoxContainer>
|
||||||
|
<controls:StripeBack>
|
||||||
|
<BoxContainer Name="DangerZone" Margin="5" Orientation="Horizontal" HorizontalExpand="True" HorizontalAlignment="Center" Visible="False">
|
||||||
|
<Button Name="DisableButton" Text="{Loc 'robotics-console-disable'}" StyleClasses="OpenRight"/>
|
||||||
|
<Button Name="DestroyButton" Text="{Loc 'robotics-console-destroy'}" StyleClasses="OpenLeft"/>
|
||||||
|
</BoxContainer>
|
||||||
|
<Label Name="LockedMessage" Text="{Loc 'robotics-console-locked-message'}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</controls:StripeBack>
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</controls:FancyWindow>
|
||||||
148
Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs
Normal file
148
Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Content.Shared.Lock;
|
||||||
|
using Content.Shared.Robotics;
|
||||||
|
using Content.Shared.Robotics.Components;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Robotics.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class RoboticsConsoleWindow : FancyWindow
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
|
private readonly LockSystem _lock;
|
||||||
|
private readonly SpriteSystem _sprite;
|
||||||
|
|
||||||
|
public Action<string>? OnDisablePressed;
|
||||||
|
public Action<string>? OnDestroyPressed;
|
||||||
|
|
||||||
|
private Entity<RoboticsConsoleComponent, LockComponent?> _console;
|
||||||
|
private string? _selected;
|
||||||
|
private Dictionary<string, CyborgControlData> _cyborgs = new();
|
||||||
|
|
||||||
|
public RoboticsConsoleWindow(EntityUid console)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
_lock = _entMan.System<LockSystem>();
|
||||||
|
_sprite = _entMan.System<SpriteSystem>();
|
||||||
|
|
||||||
|
_console = (console, _entMan.GetComponent<RoboticsConsoleComponent>(console), null);
|
||||||
|
_entMan.TryGetComponent(_console, out _console.Comp2);
|
||||||
|
|
||||||
|
Cyborgs.OnItemSelected += args =>
|
||||||
|
{
|
||||||
|
if (Cyborgs[args.ItemIndex].Metadata is not string address)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_selected = address;
|
||||||
|
PopulateData();
|
||||||
|
};
|
||||||
|
Cyborgs.OnItemDeselected += _ =>
|
||||||
|
{
|
||||||
|
_selected = null;
|
||||||
|
PopulateData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// these won't throw since buttons are only visible if a borg is selected
|
||||||
|
DisableButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
OnDisablePressed?.Invoke(_selected!);
|
||||||
|
};
|
||||||
|
DestroyButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
OnDestroyPressed?.Invoke(_selected!);
|
||||||
|
};
|
||||||
|
|
||||||
|
// cant put multiple styles in xaml for some reason
|
||||||
|
DestroyButton.StyleClasses.Add(StyleBase.ButtonCaution);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(RoboticsConsoleState state)
|
||||||
|
{
|
||||||
|
_cyborgs = state.Cyborgs;
|
||||||
|
|
||||||
|
// clear invalid selection
|
||||||
|
if (_selected is {} selected && !_cyborgs.ContainsKey(selected))
|
||||||
|
_selected = null;
|
||||||
|
|
||||||
|
var hasCyborgs = _cyborgs.Count > 0;
|
||||||
|
NoCyborgs.Visible = !hasCyborgs;
|
||||||
|
CyborgsContainer.Visible = hasCyborgs;
|
||||||
|
PopulateCyborgs();
|
||||||
|
|
||||||
|
PopulateData();
|
||||||
|
|
||||||
|
var locked = _lock.IsLocked((_console, _console.Comp2));
|
||||||
|
DangerZone.Visible = !locked;
|
||||||
|
LockedMessage.Visible = locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateCyborgs()
|
||||||
|
{
|
||||||
|
// _selected might get set to null when recreating so copy it first
|
||||||
|
var selected = _selected;
|
||||||
|
Cyborgs.Clear();
|
||||||
|
foreach (var (address, data) in _cyborgs)
|
||||||
|
{
|
||||||
|
var item = Cyborgs.AddItem(data.Name, _sprite.Frame0(data.ChassisSprite!), metadata: address);
|
||||||
|
item.Selected = address == selected;
|
||||||
|
}
|
||||||
|
_selected = selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateData()
|
||||||
|
{
|
||||||
|
if (_selected is not {} selected)
|
||||||
|
{
|
||||||
|
SelectCyborg.Visible = true;
|
||||||
|
BorgContainer.Visible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectCyborg.Visible = false;
|
||||||
|
BorgContainer.Visible = true;
|
||||||
|
|
||||||
|
var data = _cyborgs[selected];
|
||||||
|
var model = data.ChassisName;
|
||||||
|
|
||||||
|
BorgSprite.Texture = _sprite.Frame0(data.ChassisSprite!);
|
||||||
|
|
||||||
|
var batteryColor = data.Charge switch {
|
||||||
|
< 0.2f => "red",
|
||||||
|
< 0.4f => "orange",
|
||||||
|
< 0.6f => "yellow",
|
||||||
|
< 0.8f => "green",
|
||||||
|
_ => "blue"
|
||||||
|
};
|
||||||
|
|
||||||
|
var text = new FormattedMessage();
|
||||||
|
text.PushMarkup(Loc.GetString("robotics-console-model", ("name", model)));
|
||||||
|
text.AddMarkup(Loc.GetString("robotics-console-designation"));
|
||||||
|
text.AddText($" {data.Name}\n"); // prevent players trolling by naming borg [color=red]satan[/color]
|
||||||
|
text.PushMarkup(Loc.GetString("robotics-console-battery", ("charge", (int) (data.Charge * 100f)), ("color", batteryColor)));
|
||||||
|
text.PushMarkup(Loc.GetString("robotics-console-brain", ("brain", data.HasBrain)));
|
||||||
|
text.AddMarkup(Loc.GetString("robotics-console-modules", ("count", data.ModuleCount)));
|
||||||
|
BorgInfo.SetMessage(text);
|
||||||
|
|
||||||
|
// how the turntables
|
||||||
|
DisableButton.Disabled = !data.HasBrain;
|
||||||
|
DestroyButton.Disabled = _timing.CurTime < _console.Comp1.NextDestroy;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
|
DestroyButton.Disabled = _timing.CurTime < _console.Comp1.NextDestroy;
|
||||||
|
}
|
||||||
|
}
|
||||||
146
Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs
Normal file
146
Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
using Content.Server.Administration.Logs;
|
||||||
|
using Content.Server.DeviceNetwork;
|
||||||
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
|
using Content.Server.Radio.EntitySystems;
|
||||||
|
using Content.Shared.Lock;
|
||||||
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.DeviceNetwork;
|
||||||
|
using Content.Shared.Robotics;
|
||||||
|
using Content.Shared.Robotics.Components;
|
||||||
|
using Content.Shared.Robotics.Systems;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Content.Server.Research.Systems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles UI and state receiving for the robotics control console.
|
||||||
|
/// <c>BorgTransponderComponent<c/> broadcasts state from the station's borgs to consoles.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RoboticsConsoleSystem : SharedRoboticsConsoleSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly DeviceNetworkSystem _deviceNetwork = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly LockSystem _lock = default!;
|
||||||
|
[Dependency] private readonly RadioSystem _radio = default!;
|
||||||
|
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||||
|
|
||||||
|
// almost never timing out more than 1 per tick so initialize with that capacity
|
||||||
|
private List<string> _removing = new(1);
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<RoboticsConsoleComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
||||||
|
Subs.BuiEvents<RoboticsConsoleComponent>(RoboticsConsoleUiKey.Key, subs =>
|
||||||
|
{
|
||||||
|
subs.Event<BoundUIOpenedEvent>(OnOpened);
|
||||||
|
subs.Event<RoboticsConsoleDisableMessage>(OnDisable);
|
||||||
|
subs.Event<RoboticsConsoleDestroyMessage>(OnDestroy);
|
||||||
|
// TODO: camera stuff
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var now = _timing.CurTime;
|
||||||
|
var query = EntityQueryEnumerator<RoboticsConsoleComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var comp))
|
||||||
|
{
|
||||||
|
// remove cyborgs that havent pinged in a while
|
||||||
|
_removing.Clear();
|
||||||
|
foreach (var (address, data) in comp.Cyborgs)
|
||||||
|
{
|
||||||
|
if (now >= data.Timeout)
|
||||||
|
_removing.Add(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
// needed to prevent modifying while iterating it
|
||||||
|
foreach (var address in _removing)
|
||||||
|
{
|
||||||
|
comp.Cyborgs.Remove(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_removing.Count > 0)
|
||||||
|
UpdateUserInterface((uid, comp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPacketReceived(Entity<RoboticsConsoleComponent> ent, ref DeviceNetworkPacketEvent args)
|
||||||
|
{
|
||||||
|
var payload = args.Data;
|
||||||
|
if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command))
|
||||||
|
return;
|
||||||
|
if (command != DeviceNetworkConstants.CmdUpdatedState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!payload.TryGetValue(RoboticsConsoleConstants.NET_CYBORG_DATA, out CyborgControlData? data))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var real = data.Value;
|
||||||
|
real.Timeout = _timing.CurTime + ent.Comp.Timeout;
|
||||||
|
ent.Comp.Cyborgs[args.SenderAddress] = real;
|
||||||
|
|
||||||
|
UpdateUserInterface(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOpened(Entity<RoboticsConsoleComponent> ent, ref BoundUIOpenedEvent args)
|
||||||
|
{
|
||||||
|
UpdateUserInterface(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable(Entity<RoboticsConsoleComponent> ent, ref RoboticsConsoleDisableMessage args)
|
||||||
|
{
|
||||||
|
if (_lock.IsLocked(ent.Owner))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ent.Comp.Cyborgs.TryGetValue(args.Address, out var data))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var payload = new NetworkPayload()
|
||||||
|
{
|
||||||
|
[DeviceNetworkConstants.Command] = RoboticsConsoleConstants.NET_DISABLE_COMMAND
|
||||||
|
};
|
||||||
|
|
||||||
|
_deviceNetwork.QueuePacket(ent, args.Address, payload);
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):user} disabled borg {data.Name} with address {args.Address}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy(Entity<RoboticsConsoleComponent> ent, ref RoboticsConsoleDestroyMessage args)
|
||||||
|
{
|
||||||
|
if (_lock.IsLocked(ent.Owner))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var now = _timing.CurTime;
|
||||||
|
if (now < ent.Comp.NextDestroy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ent.Comp.Cyborgs.Remove(args.Address, out var data))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var payload = new NetworkPayload()
|
||||||
|
{
|
||||||
|
[DeviceNetworkConstants.Command] = RoboticsConsoleConstants.NET_DESTROY_COMMAND
|
||||||
|
};
|
||||||
|
|
||||||
|
_deviceNetwork.QueuePacket(ent, args.Address, payload);
|
||||||
|
|
||||||
|
var message = Loc.GetString(ent.Comp.DestroyMessage, ("name", data.Name));
|
||||||
|
_radio.SendRadioMessage(ent, message, ent.Comp.RadioChannel, ent);
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(args.Actor):user} destroyed borg {data.Name} with address {args.Address}");
|
||||||
|
|
||||||
|
ent.Comp.NextDestroy = now + ent.Comp.DestroyCooldown;
|
||||||
|
Dirty(ent, ent.Comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateUserInterface(Entity<RoboticsConsoleComponent> ent)
|
||||||
|
{
|
||||||
|
var state = new RoboticsConsoleState(ent.Comp.Cyborgs);
|
||||||
|
_ui.SetUiState(ent.Owner, RoboticsConsoleUiKey.Key, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs
Normal file
107
Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
using Content.Shared.DeviceNetwork;
|
||||||
|
using Content.Shared.Emag.Components;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Robotics;
|
||||||
|
using Content.Shared.Silicons.Borgs.Components;
|
||||||
|
using Content.Server.DeviceNetwork;
|
||||||
|
using Content.Server.DeviceNetwork.Components;
|
||||||
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
|
using Content.Server.Explosion.Components;
|
||||||
|
|
||||||
|
namespace Content.Server.Silicons.Borgs;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public sealed partial class BorgSystem
|
||||||
|
{
|
||||||
|
private void InitializeTransponder()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<BorgTransponderComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var now = _timing.CurTime;
|
||||||
|
var query = EntityQueryEnumerator<BorgTransponderComponent, BorgChassisComponent, DeviceNetworkComponent, MetaDataComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var comp, out var chassis, out var device, out var meta))
|
||||||
|
{
|
||||||
|
if (now < comp.NextBroadcast)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var charge = 0f;
|
||||||
|
if (_powerCell.TryGetBatteryFromSlot(uid, out var battery))
|
||||||
|
charge = battery.CurrentCharge / battery.MaxCharge;
|
||||||
|
|
||||||
|
var data = new CyborgControlData(
|
||||||
|
comp.Sprite,
|
||||||
|
comp.Name,
|
||||||
|
meta.EntityName,
|
||||||
|
charge,
|
||||||
|
chassis.ModuleCount,
|
||||||
|
chassis.BrainEntity != null);
|
||||||
|
|
||||||
|
var payload = new NetworkPayload()
|
||||||
|
{
|
||||||
|
[DeviceNetworkConstants.Command] = DeviceNetworkConstants.CmdUpdatedState,
|
||||||
|
[RoboticsConsoleConstants.NET_CYBORG_DATA] = data
|
||||||
|
};
|
||||||
|
_deviceNetwork.QueuePacket(uid, null, payload, device: device);
|
||||||
|
|
||||||
|
comp.NextBroadcast = now + comp.BroadcastDelay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPacketReceived(Entity<BorgTransponderComponent> ent, ref DeviceNetworkPacketEvent args)
|
||||||
|
{
|
||||||
|
var payload = args.Data;
|
||||||
|
if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (command == RoboticsConsoleConstants.NET_DISABLE_COMMAND)
|
||||||
|
Disable(ent);
|
||||||
|
else if (command == RoboticsConsoleConstants.NET_DESTROY_COMMAND)
|
||||||
|
Destroy(ent.Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Disable(Entity<BorgTransponderComponent, BorgChassisComponent?> ent)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp2) || ent.Comp2.BrainEntity is not {} brain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// this won't exactly be stealthy but if you are malf its better than actually disabling you
|
||||||
|
if (CheckEmagged(ent, "disabled"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var message = Loc.GetString(ent.Comp1.DisabledPopup, ("name", Name(ent)));
|
||||||
|
Popup.PopupEntity(message, ent);
|
||||||
|
_container.Remove(brain, ent.Comp2.BrainContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Destroy(Entity<ExplosiveComponent?> ent)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// this is stealthy until someone realises you havent exploded
|
||||||
|
if (CheckEmagged(ent, "destroyed"))
|
||||||
|
{
|
||||||
|
// prevent reappearing on the console a few seconds later
|
||||||
|
RemComp<BorgTransponderComponent>(ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_explosion.TriggerExplosive(ent, ent.Comp, delete: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckEmagged(EntityUid uid, string name)
|
||||||
|
{
|
||||||
|
if (HasComp<EmaggedComponent>(uid))
|
||||||
|
{
|
||||||
|
Popup.PopupEntity(Loc.GetString($"borg-transponder-emagged-{name}-popup"), uid, uid, PopupType.LargeCaution);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
using Content.Server.Actions;
|
using Content.Server.Actions;
|
||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Administration.Managers;
|
using Content.Server.Administration.Managers;
|
||||||
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
|
using Content.Server.Explosion.EntitySystems;
|
||||||
using Content.Server.Hands.Systems;
|
using Content.Server.Hands.Systems;
|
||||||
using Content.Server.PowerCell;
|
using Content.Server.PowerCell;
|
||||||
using Content.Shared.UserInterface;
|
using Content.Shared.UserInterface;
|
||||||
@@ -26,6 +28,7 @@ using Robust.Server.GameObjects;
|
|||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Silicons.Borgs;
|
namespace Content.Server.Silicons.Borgs;
|
||||||
|
|
||||||
@@ -34,10 +37,13 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
||||||
[Dependency] private readonly IBanManager _banManager = default!;
|
[Dependency] private readonly IBanManager _banManager = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly SharedAccessSystem _access = default!;
|
[Dependency] private readonly SharedAccessSystem _access = default!;
|
||||||
[Dependency] private readonly ActionsSystem _actions = default!;
|
[Dependency] private readonly ActionsSystem _actions = default!;
|
||||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||||
|
[Dependency] private readonly DeviceNetworkSystem _deviceNetwork = default!;
|
||||||
|
[Dependency] private readonly ExplosionSystem _explosion = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly HandsSystem _hands = default!;
|
[Dependency] private readonly HandsSystem _hands = default!;
|
||||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||||
@@ -73,6 +79,7 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
|||||||
InitializeModules();
|
InitializeModules();
|
||||||
InitializeMMI();
|
InitializeMMI();
|
||||||
InitializeUI();
|
InitializeUI();
|
||||||
|
InitializeTransponder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(EntityUid uid, BorgChassisComponent component, MapInitEvent args)
|
private void OnMapInit(EntityUid uid, BorgChassisComponent component, MapInitEvent args)
|
||||||
|
|||||||
@@ -21,12 +21,18 @@ public sealed partial class LockComponent : Component
|
|||||||
public bool Locked = true;
|
public bool Locked = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not the lock is toggled by simply clicking.
|
/// Whether or not the lock is locked by simply clicking.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("lockOnClick"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField("lockOnClick"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
[AutoNetworkedField]
|
[AutoNetworkedField]
|
||||||
public bool LockOnClick;
|
public bool LockOnClick;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not the lock is unlocked by simply clicking.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool UnlockOnClick = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sound played when unlocked.
|
/// The sound played when unlocked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -58,12 +58,12 @@ public sealed class LockSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Only attempt an unlock by default on Activate
|
// Only attempt an unlock by default on Activate
|
||||||
if (lockComp.Locked)
|
if (lockComp.Locked && lockComp.UnlockOnClick)
|
||||||
{
|
{
|
||||||
TryUnlock(uid, args.User, lockComp);
|
TryUnlock(uid, args.User, lockComp);
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
else if (lockComp.LockOnClick)
|
else if (!lockComp.Locked && lockComp.LockOnClick)
|
||||||
{
|
{
|
||||||
TryLock(uid, args.User, lockComp);
|
TryLock(uid, args.User, lockComp);
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
@@ -201,6 +201,18 @@ public sealed class LockSystem : EntitySystem
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the entity is locked.
|
||||||
|
/// Entities with no lock component are considered unlocked.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLocked(Entity<LockComponent?> ent)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ent.Comp.Locked;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raises an event for other components to check whether or not
|
/// Raises an event for other components to check whether or not
|
||||||
/// the entity can be locked in its current state.
|
/// the entity can be locked in its current state.
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using Content.Shared.Radio;
|
||||||
|
using Content.Shared.Robotics;
|
||||||
|
using Content.Shared.Robotics.Systems;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Shared.Robotics.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Robotics console for managing borgs.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRoboticsConsoleSystem))]
|
||||||
|
[AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||||
|
public sealed partial class RoboticsConsoleComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Address and data of each cyborg.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public Dictionary<string, CyborgControlData> Cyborgs = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// After not responding for this length of time borgs are removed from the console.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan Timeout = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Radio channel to send messages on.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ProtoId<RadioChannelPrototype> RadioChannel = "Science";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Radio message sent when destroying a borg.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public LocId DestroyMessage = "robotics-console-cyborg-destroyed";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cooldown on destroying borgs to prevent complete abuse.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan DestroyCooldown = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When a borg can next be destroyed.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
|
[AutoNetworkedField, AutoPausedField]
|
||||||
|
public TimeSpan NextDestroy = TimeSpan.Zero;
|
||||||
|
}
|
||||||
126
Content.Shared/Robotics/RoboticsConsoleUi.cs
Normal file
126
Content.Shared/Robotics/RoboticsConsoleUi.cs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.Robotics;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum RoboticsConsoleUiKey : byte
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class RoboticsConsoleState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Map of device network addresses to cyborg data.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, CyborgControlData> Cyborgs;
|
||||||
|
|
||||||
|
public RoboticsConsoleState(Dictionary<string, CyborgControlData> cyborgs)
|
||||||
|
{
|
||||||
|
Cyborgs = cyborgs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Message to disable the selected cyborg.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class RoboticsConsoleDisableMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly string Address;
|
||||||
|
|
||||||
|
public RoboticsConsoleDisableMessage(string address)
|
||||||
|
{
|
||||||
|
Address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Message to destroy the selected cyborg.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class RoboticsConsoleDestroyMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly string Address;
|
||||||
|
|
||||||
|
public RoboticsConsoleDestroyMessage(string address)
|
||||||
|
{
|
||||||
|
Address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All data a client needs to render the console UI for a single cyborg.
|
||||||
|
/// Created by <c>BorgTransponderComponent</c> and sent to clients by <c>RoboticsConsoleComponent</c>.
|
||||||
|
/// </summary>
|
||||||
|
[DataRecord, Serializable, NetSerializable]
|
||||||
|
public record struct CyborgControlData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Texture of the borg chassis.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public SpriteSpecifier? ChassisSprite;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the borg chassis.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public string ChassisName = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the borg's entity, including its silicon id.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public string Name = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Battery charge from 0 to 1.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float Charge;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How many modules this borg has, just useful information for roboticists.
|
||||||
|
/// Lets them keep track of the latejoin borgs that need new modules and stuff.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int ModuleCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the borg has a brain installed or not.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool HasBrain;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When this cyborg's data will be deleted.
|
||||||
|
/// Set by the console when receiving the packet.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
|
public TimeSpan Timeout = TimeSpan.Zero;
|
||||||
|
|
||||||
|
public CyborgControlData(SpriteSpecifier? chassisSprite, string chassisName, string name, float charge, int moduleCount, bool hasBrain)
|
||||||
|
{
|
||||||
|
ChassisSprite = chassisSprite;
|
||||||
|
ChassisName = chassisName;
|
||||||
|
Name = name;
|
||||||
|
Charge = charge;
|
||||||
|
ModuleCount = moduleCount;
|
||||||
|
HasBrain = hasBrain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RoboticsConsoleConstants
|
||||||
|
{
|
||||||
|
// broadcast by cyborgs on Robotics Console frequency
|
||||||
|
public const string NET_CYBORG_DATA = "cyborg-data";
|
||||||
|
|
||||||
|
// sent by robotics console to cyborgs on Cyborg Control frequency
|
||||||
|
public const string NET_DISABLE_COMMAND = "cyborg-disable";
|
||||||
|
public const string NET_DESTROY_COMMAND = "cyborg-destroy";
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Content.Shared.Robotics.Systems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Does nothing, only exists for access right now.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SharedRoboticsConsoleSystem : EntitySystem
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.Silicons.Borgs.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Periodically broadcasts borg data to robotics consoles.
|
||||||
|
/// When not emagged, handles disabling and destroying commands as expected.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(SharedBorgSystem))]
|
||||||
|
public sealed partial class BorgTransponderComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sprite of the chassis to send.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public SpriteSpecifier? Sprite;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the chassis to send.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public string Name = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Popup shown to everyone when a borg is disabled.
|
||||||
|
/// Gets passed a string "name".
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public LocId DisabledPopup = "borg-transponder-disabled-popup";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long to wait between each broadcast.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan BroadcastDelay = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When to next broadcast data.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
|
public TimeSpan NextBroadcast = TimeSpan.Zero;
|
||||||
|
}
|
||||||
@@ -17,3 +17,8 @@ borg-ui-no-brain = No brain present
|
|||||||
borg-ui-remove-battery = Remove
|
borg-ui-remove-battery = Remove
|
||||||
borg-ui-modules-label = Modules:
|
borg-ui-modules-label = Modules:
|
||||||
borg-ui-module-counter = {$actual}/{$max}
|
borg-ui-module-counter = {$actual}/{$max}
|
||||||
|
|
||||||
|
# Transponder
|
||||||
|
borg-transponder-disabled-popup = A brain shoots out the top of {$name}!
|
||||||
|
borg-transponder-emagged-disabled-popup = Your transponder's lights go out!
|
||||||
|
borg-transponder-emagged-destroyed-popup = Your transponder's fuse blows!
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ device-frequency-prototype-name-mailing-units = Mailing Units
|
|||||||
device-frequency-prototype-name-pdas = PDAs
|
device-frequency-prototype-name-pdas = PDAs
|
||||||
device-frequency-prototype-name-fax = Fax
|
device-frequency-prototype-name-fax = Fax
|
||||||
device-frequency-prototype-name-basic-device = Basic Devices
|
device-frequency-prototype-name-basic-device = Basic Devices
|
||||||
|
device-frequency-prototype-name-cyborg-control = Cyborg Control
|
||||||
|
device-frequency-prototype-name-robotics-console = Robotics Console
|
||||||
|
|
||||||
## camera frequencies
|
## camera frequencies
|
||||||
device-frequency-prototype-name-surveillance-camera-test = Subnet Test
|
device-frequency-prototype-name-surveillance-camera-test = Subnet Test
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
robotics-console-window-title = Robotics Console
|
||||||
|
robotics-console-no-cyborgs = No Cyborgs!
|
||||||
|
|
||||||
|
robotics-console-select-cyborg = Select a cyborg above.
|
||||||
|
robotics-console-model = [color=gray]Model:[/color] {$name}
|
||||||
|
# name is not formatted to prevent players trolling
|
||||||
|
robotics-console-designation = [color=gray]Designation:[/color]
|
||||||
|
robotics-console-battery = [color=gray]Battery charge:[/color] [color={$color}]{$charge}[/color]%
|
||||||
|
robotics-console-modules = [color=gray]Modules installed:[/color] {$count}
|
||||||
|
robotics-console-brain = [color=gray]Brain installed:[/color] [color={$brain ->
|
||||||
|
[true] green]Yes
|
||||||
|
*[false] red]No
|
||||||
|
}[/color]
|
||||||
|
|
||||||
|
robotics-console-locked-message = Controls locked, swipe ID.
|
||||||
|
robotics-console-disable = Disable
|
||||||
|
robotics-console-destroy = Destroy
|
||||||
|
|
||||||
|
robotics-console-cyborg-destroyed = The cyborg {$name} has been remotely destroyed.
|
||||||
@@ -75,6 +75,17 @@
|
|||||||
name: device-frequency-prototype-name-crew-monitor
|
name: device-frequency-prototype-name-crew-monitor
|
||||||
frequency: 1261
|
frequency: 1261
|
||||||
|
|
||||||
|
# Cyborgs broadcast to consoles on this frequency
|
||||||
|
- type: deviceFrequency
|
||||||
|
id: RoboticsConsole
|
||||||
|
name: device-frequency-prototype-name-robotics-console
|
||||||
|
frequency: 1291
|
||||||
|
|
||||||
|
# Console sends commands to cyborgs on this frequency
|
||||||
|
- type: deviceFrequency
|
||||||
|
id: CyborgControl
|
||||||
|
name: device-frequency-prototype-name-cyborg-control
|
||||||
|
frequency: 1292
|
||||||
|
|
||||||
# This frequency will likely have a LARGE number of listening entities. Please don't broadcast on this frequency.
|
# This frequency will likely have a LARGE number of listening entities. Please don't broadcast on this frequency.
|
||||||
- type: deviceFrequency
|
- type: deviceFrequency
|
||||||
|
|||||||
@@ -217,9 +217,26 @@
|
|||||||
- Cyborgs
|
- Cyborgs
|
||||||
- type: StepTriggerImmune
|
- type: StepTriggerImmune
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
abstract: true
|
||||||
|
id: BaseBorgTransponder
|
||||||
|
components:
|
||||||
|
- type: BorgTransponder
|
||||||
|
- type: DeviceNetwork
|
||||||
|
deviceNetId: Wireless
|
||||||
|
receiveFrequencyId: CyborgControl
|
||||||
|
transmitFrequencyId: RoboticsConsole
|
||||||
|
# explosion does most of its damage in the center and less at the edges
|
||||||
|
- type: Explosive
|
||||||
|
explosionType: Minibomb
|
||||||
|
totalIntensity: 30
|
||||||
|
intensitySlope: 20
|
||||||
|
maxIntensity: 20
|
||||||
|
canCreateVacuum: false # its for killing the borg not the station
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: BaseBorgChassisNT
|
id: BaseBorgChassisNT
|
||||||
parent: BaseBorgChassis
|
parent: [BaseBorgChassis, BaseBorgTransponder]
|
||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
- type: NpcFactionMember
|
- type: NpcFactionMember
|
||||||
|
|||||||
@@ -20,6 +20,11 @@
|
|||||||
- BorgModuleGeneric
|
- BorgModuleGeneric
|
||||||
hasMindState: robot_e
|
hasMindState: robot_e
|
||||||
noMindState: robot_e_r
|
noMindState: robot_e_r
|
||||||
|
- type: BorgTransponder
|
||||||
|
sprite:
|
||||||
|
sprite: Mobs/Silicon/chassis.rsi
|
||||||
|
state: robot
|
||||||
|
name: cyborg
|
||||||
- type: Construction
|
- type: Construction
|
||||||
node: cyborg
|
node: cyborg
|
||||||
- type: Speech
|
- type: Speech
|
||||||
@@ -57,6 +62,11 @@
|
|||||||
- BorgModuleCargo
|
- BorgModuleCargo
|
||||||
hasMindState: miner_e
|
hasMindState: miner_e
|
||||||
noMindState: miner_e_r
|
noMindState: miner_e_r
|
||||||
|
- type: BorgTransponder
|
||||||
|
sprite:
|
||||||
|
sprite: Mobs/Silicon/chassis.rsi
|
||||||
|
state: miner
|
||||||
|
name: salvage cyborg
|
||||||
- type: Construction
|
- type: Construction
|
||||||
node: mining
|
node: mining
|
||||||
- type: IntrinsicRadioTransmitter
|
- type: IntrinsicRadioTransmitter
|
||||||
@@ -100,6 +110,11 @@
|
|||||||
- BorgModuleEngineering
|
- BorgModuleEngineering
|
||||||
hasMindState: engineer_e
|
hasMindState: engineer_e
|
||||||
noMindState: engineer_e_r
|
noMindState: engineer_e_r
|
||||||
|
- type: BorgTransponder
|
||||||
|
sprite:
|
||||||
|
sprite: Mobs/Silicon/chassis.rsi
|
||||||
|
state: engineer
|
||||||
|
name: engineer cyborg
|
||||||
- type: Construction
|
- type: Construction
|
||||||
node: engineer
|
node: engineer
|
||||||
- type: IntrinsicRadioTransmitter
|
- type: IntrinsicRadioTransmitter
|
||||||
@@ -151,6 +166,11 @@
|
|||||||
- BorgModuleJanitor
|
- BorgModuleJanitor
|
||||||
hasMindState: janitor_e
|
hasMindState: janitor_e
|
||||||
noMindState: janitor_e_r
|
noMindState: janitor_e_r
|
||||||
|
- type: BorgTransponder
|
||||||
|
sprite:
|
||||||
|
sprite: Mobs/Silicon/chassis.rsi
|
||||||
|
state: janitor
|
||||||
|
name: janitor cyborg
|
||||||
- type: Construction
|
- type: Construction
|
||||||
node: janitor
|
node: janitor
|
||||||
- type: IntrinsicRadioTransmitter
|
- type: IntrinsicRadioTransmitter
|
||||||
@@ -202,6 +222,11 @@
|
|||||||
- BorgModuleMedical
|
- BorgModuleMedical
|
||||||
hasMindState: medical_e
|
hasMindState: medical_e
|
||||||
noMindState: medical_e_r
|
noMindState: medical_e_r
|
||||||
|
- type: BorgTransponder
|
||||||
|
sprite:
|
||||||
|
sprite: Mobs/Silicon/chassis.rsi
|
||||||
|
state: medical
|
||||||
|
name: medical cyborg
|
||||||
- type: Construction
|
- type: Construction
|
||||||
node: medical
|
node: medical
|
||||||
- type: IntrinsicRadioTransmitter
|
- type: IntrinsicRadioTransmitter
|
||||||
@@ -250,6 +275,11 @@
|
|||||||
- BorgModuleService
|
- BorgModuleService
|
||||||
hasMindState: service_e
|
hasMindState: service_e
|
||||||
noMindState: service_e_r
|
noMindState: service_e_r
|
||||||
|
- type: BorgTransponder
|
||||||
|
sprite:
|
||||||
|
sprite: Mobs/Silicon/chassis.rsi
|
||||||
|
state: service
|
||||||
|
name: service cyborg
|
||||||
- type: Construction
|
- type: Construction
|
||||||
node: service
|
node: service
|
||||||
- type: IntrinsicRadioTransmitter
|
- type: IntrinsicRadioTransmitter
|
||||||
|
|||||||
@@ -384,3 +384,14 @@
|
|||||||
state: cpu_engineering
|
state: cpu_engineering
|
||||||
- type: ComputerBoard
|
- type: ComputerBoard
|
||||||
prototype: ComputerSensorMonitoring
|
prototype: ComputerSensorMonitoring
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BaseComputerCircuitboard
|
||||||
|
id: RoboticsConsoleCircuitboard
|
||||||
|
name: robotics control console board
|
||||||
|
description: A computer printed circuit board for a robotics control console.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: cpu_science
|
||||||
|
- type: ComputerBoard
|
||||||
|
prototype: ComputerRoboticsControl
|
||||||
|
|||||||
@@ -1076,3 +1076,42 @@
|
|||||||
- type: WiredNetworkConnection
|
- type: WiredNetworkConnection
|
||||||
- type: DeviceList
|
- type: DeviceList
|
||||||
- type: AtmosDevice
|
- type: AtmosDevice
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BaseComputer
|
||||||
|
id: ComputerRoboticsControl
|
||||||
|
name: robotics control console
|
||||||
|
description: Used to remotely monitor, disable and destroy the station's cyborgs.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
layers:
|
||||||
|
- map: ["computerLayerBody"]
|
||||||
|
state: computer
|
||||||
|
- map: ["computerLayerKeyboard"]
|
||||||
|
state: generic_keyboard
|
||||||
|
- map: ["computerLayerScreen"]
|
||||||
|
state: robot
|
||||||
|
- map: ["computerLayerKeys"]
|
||||||
|
state: rd_key
|
||||||
|
- type: RoboticsConsole
|
||||||
|
- type: ActiveRadio
|
||||||
|
channels:
|
||||||
|
- Science
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.RoboticsConsoleUiKey.Key
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
enum.RoboticsConsoleUiKey.Key:
|
||||||
|
type: RoboticsConsoleBoundUserInterface
|
||||||
|
- type: ApcPowerReceiver
|
||||||
|
powerLoad: 1000
|
||||||
|
- type: DeviceNetwork
|
||||||
|
deviceNetId: Wireless
|
||||||
|
receiveFrequencyId: RoboticsConsole
|
||||||
|
transmitFrequencyId: CyborgControl
|
||||||
|
- type: Computer
|
||||||
|
board: RoboticsConsoleCircuitboard
|
||||||
|
- type: AccessReader # only used for dangerous things
|
||||||
|
access: [["ResearchDirector"]]
|
||||||
|
- type: Lock
|
||||||
|
unlockOnClick: false
|
||||||
|
|||||||
Reference in New Issue
Block a user