mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
Predict CartridgeLoader (#40302)
* Move CartridgeLoader to shared * get containered bozo * the dirtening * mass shared the cartridges * predict keb cartridges * no overpopups * the tidying continues * review * aaaaaa * fix UI flickers * dependency me * mimed * refactor: removed unused usings, dots at the end of old xml-doc, additional in line-breaks --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Co-authored-by: Fildrance <fildrance@gmail.com>
This commit is contained in:
parent
f563a6a2ce
commit
09fb1b923f
42 changed files with 904 additions and 1046 deletions
|
|
@ -1,6 +1,5 @@
|
|||
using Content.Client.UserInterface.Fragments;
|
||||
using Content.Client.UserInterface.Fragments;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.CartridgeLoader;
|
||||
|
|
@ -67,7 +66,7 @@ public abstract class CartridgeLoaderBoundUserInterface : BoundUserInterface
|
|||
protected void ActivateCartridge(EntityUid cartridgeUid)
|
||||
{
|
||||
var message = new CartridgeLoaderUiMessage(_entManager.GetNetEntity(cartridgeUid), CartridgeUiMessageAction.Activate);
|
||||
SendMessage(message);
|
||||
SendPredictedMessage(message);
|
||||
}
|
||||
|
||||
protected void DeactivateActiveCartridge()
|
||||
|
|
@ -76,19 +75,19 @@ public abstract class CartridgeLoaderBoundUserInterface : BoundUserInterface
|
|||
return;
|
||||
|
||||
var message = new CartridgeLoaderUiMessage(_entManager.GetNetEntity(_activeProgram.Value), CartridgeUiMessageAction.Deactivate);
|
||||
SendMessage(message);
|
||||
SendPredictedMessage(message);
|
||||
}
|
||||
|
||||
protected void InstallCartridge(EntityUid cartridgeUid)
|
||||
{
|
||||
var message = new CartridgeLoaderUiMessage(_entManager.GetNetEntity(cartridgeUid), CartridgeUiMessageAction.Install);
|
||||
SendMessage(message);
|
||||
SendPredictedMessage(message);
|
||||
}
|
||||
|
||||
protected void UninstallCartridge(EntityUid cartridgeUid)
|
||||
{
|
||||
var message = new CartridgeLoaderUiMessage(_entManager.GetNetEntity(cartridgeUid), CartridgeUiMessageAction.Uninstall);
|
||||
SendMessage(message);
|
||||
SendPredictedMessage(message);
|
||||
}
|
||||
|
||||
private List<(EntityUid, CartridgeComponent)> GetCartridgeComponents(List<EntityUid> programs)
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
using Content.Shared.CartridgeLoader;
|
||||
|
||||
namespace Content.Client.CartridgeLoader;
|
||||
|
||||
public sealed class CartridgeLoaderSystem : SharedCartridgeLoaderSystem
|
||||
{
|
||||
//Empty client system for component replication
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ public sealed partial class LogProbeUi : UIFragment
|
|||
{
|
||||
var ev = new LogProbePrintMessage();
|
||||
var message = new CartridgeUiMessage(ev);
|
||||
ui.SendMessage(message);
|
||||
ui.SendPredictedMessage(message);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
namespace Content.Client.CartridgeLoader.Cartridges;
|
||||
|
||||
public sealed class NanoTaskCartridgeSystem : SharedNanoTaskCartridgeSystem;
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
using System.Linq;
|
||||
using Content.Client.UserInterface.Fragments;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.CartridgeLoader.Cartridges;
|
||||
|
|
@ -44,7 +42,7 @@ public sealed partial class NanoTaskUi : UIFragment
|
|||
if (_fragment.Tasks.Find(task => task.Id == id) is not NanoTaskItemAndId task)
|
||||
return;
|
||||
|
||||
userInterface.SendMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskUpdateTask(new(id, new(
|
||||
userInterface.SendPredictedMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskUpdateTask(new(id, new(
|
||||
description: task.Data.Description,
|
||||
taskIsFor: task.Data.TaskIsFor,
|
||||
isTaskDone: !task.Data.IsTaskDone,
|
||||
|
|
@ -53,22 +51,22 @@ public sealed partial class NanoTaskUi : UIFragment
|
|||
};
|
||||
_popup.TaskSaved += (id, data) =>
|
||||
{
|
||||
userInterface.SendMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskUpdateTask(new(id, data)))));
|
||||
userInterface.SendPredictedMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskUpdateTask(new(id, data)))));
|
||||
_popup.Close();
|
||||
};
|
||||
_popup.TaskDeleted += id =>
|
||||
{
|
||||
userInterface.SendMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskDeleteTask(id))));
|
||||
userInterface.SendPredictedMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskDeleteTask(id))));
|
||||
_popup.Close();
|
||||
};
|
||||
_popup.TaskCreated += data =>
|
||||
{
|
||||
userInterface.SendMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskAddTask(data))));
|
||||
userInterface.SendPredictedMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskAddTask(data))));
|
||||
_popup.Close();
|
||||
};
|
||||
_popup.TaskPrinted += data =>
|
||||
{
|
||||
userInterface.SendMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskPrintTask(data))));
|
||||
userInterface.SendPredictedMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskPrintTask(data))));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using Content.Client.UserInterface.Fragments;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.CartridgeLoader.Cartridges;
|
||||
|
|
@ -34,6 +33,6 @@ public sealed partial class NotekeeperUi : UIFragment
|
|||
{
|
||||
var notekeeperMessage = new NotekeeperUiMessageEvent(action, note);
|
||||
var message = new CartridgeUiMessage(notekeeperMessage);
|
||||
userInterface.SendMessage(message);
|
||||
userInterface.SendPredictedMessage(message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,12 +48,12 @@
|
|||
</ContainerButton>
|
||||
</BoxContainer>
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" HScrollEnabled="True">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
MinSize="50 50"
|
||||
Name="ProgramList"
|
||||
Margin="4"/>
|
||||
<GridContainer VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
MinSize="50 50"
|
||||
Name="ProgramList"
|
||||
Margin="4"
|
||||
Columns="2" />
|
||||
</ScrollContainer>
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" HScrollEnabled="True">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
|
|
|
|||
|
|
@ -200,10 +200,10 @@ namespace Content.Client.PDA
|
|||
|
||||
public void UpdateAvailablePrograms(List<(EntityUid, CartridgeComponent)> programs)
|
||||
{
|
||||
ProgramList.RemoveAllChildren();
|
||||
|
||||
if (programs.Count == 0)
|
||||
{
|
||||
ProgramList.RemoveAllChildren();
|
||||
|
||||
ProgramList.AddChild(new Label()
|
||||
{
|
||||
Text = Loc.GetString("comp-pda-io-no-programs-available"),
|
||||
|
|
@ -215,50 +215,43 @@ namespace Content.Client.PDA
|
|||
return;
|
||||
}
|
||||
|
||||
var row = CreateProgramListRow();
|
||||
var itemCount = 1;
|
||||
ProgramList.AddChild(row);
|
||||
|
||||
foreach (var (uid, component) in programs)
|
||||
if (ProgramList.ChildCount >= 1 && ProgramList.Children[0] is Label label)
|
||||
{
|
||||
//Create a new row every second program item starting from the first
|
||||
if (itemCount % 2 != 0)
|
||||
label.Orphan();
|
||||
}
|
||||
|
||||
while (ProgramList.ChildCount > programs.Count)
|
||||
{
|
||||
ProgramList.Children[ProgramList.ChildCount - 1].Orphan();
|
||||
}
|
||||
|
||||
for (var i = 0; i < programs.Count; i++)
|
||||
{
|
||||
var cartridge = programs[i];
|
||||
|
||||
if (i < ProgramList.ChildCount)
|
||||
{
|
||||
row = CreateProgramListRow();
|
||||
ProgramList.AddChild(row);
|
||||
var currentItem = ProgramList.Children[i];
|
||||
if (currentItem is PdaProgramItem programItem)
|
||||
{
|
||||
programItem.SetCartridge(cartridge);
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugTools.Assert(i == ProgramList.ChildCount-1);
|
||||
currentItem.Orphan();
|
||||
}
|
||||
|
||||
var item = new PdaProgramItem();
|
||||
|
||||
if (component.Icon is not null)
|
||||
item.Icon.SetFromSpriteSpecifier(component.Icon);
|
||||
|
||||
item.OnPressed += _ => OnProgramItemPressed?.Invoke(uid);
|
||||
|
||||
switch (component.InstallationStatus)
|
||||
{
|
||||
case InstallationStatus.Cartridge:
|
||||
item.InstallButton.Visible = true;
|
||||
item.InstallButton.Text = Loc.GetString("cartridge-bound-user-interface-install-button");
|
||||
item.InstallButton.OnPressed += _ => OnInstallButtonPressed?.Invoke(uid);
|
||||
break;
|
||||
case InstallationStatus.Installed:
|
||||
item.InstallButton.Visible = true;
|
||||
item.InstallButton.Text = Loc.GetString("cartridge-bound-user-interface-uninstall-button");
|
||||
item.InstallButton.OnPressed += _ => OnUninstallButtonPressed?.Invoke(uid);
|
||||
break;
|
||||
}
|
||||
|
||||
item.ProgramName.Text = Loc.GetString(component.ProgramName);
|
||||
item.SetHeight = 20;
|
||||
row.AddChild(item);
|
||||
|
||||
itemCount++;
|
||||
var item = new PdaProgramItem(cartridge);
|
||||
item.OnProgramItemPressed += uid => OnProgramItemPressed?.Invoke(uid);
|
||||
item.OnUninstallButtonPressed += uid => OnUninstallButtonPressed?.Invoke(uid);
|
||||
item.OnInstallButtonPressed += uid => OnInstallButtonPressed?.Invoke(uid);
|
||||
ProgramList.AddChild(item);
|
||||
}
|
||||
|
||||
//Add a filler item to the last row when it only contains one item
|
||||
if (itemCount % 2 == 0)
|
||||
row.AddChild(new Control() { HorizontalExpand = true });
|
||||
if (programs.Count % 2 == 0)
|
||||
ProgramList.AddChild(new Control() { HorizontalExpand = true });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -319,15 +312,6 @@ namespace Content.Client.PDA
|
|||
_currentView = view;
|
||||
}
|
||||
|
||||
private static BoxContainer CreateProgramListRow()
|
||||
{
|
||||
return new BoxContainer()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true
|
||||
};
|
||||
}
|
||||
|
||||
private void HideAllViews()
|
||||
{
|
||||
var views = ViewContainer.Children;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<pda:PdaProgramItem HorizontalExpand="True" MinHeight="60" Margin="4"
|
||||
xmlns:pda="clr-namespace:Content.Client.PDA"
|
||||
xmlns="https://spacestation14.io">
|
||||
<ContainerButton HorizontalExpand="True" MinHeight="60" Margin="4"
|
||||
xmlns:pda="clr-namespace:Content.Client.PDA"
|
||||
xmlns="https://spacestation14.io">
|
||||
<PanelContainer Name="Panel"/>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" MinWidth="60">
|
||||
<AnimatedTextureRect HorizontalAlignment="Center" VerticalAlignment="Center" VerticalExpand="True" Name="Icon" Access="Public"/>
|
||||
<AnimatedTextureRect HorizontalAlignment="Center" VerticalAlignment="Center" VerticalExpand="True" Name="Icon" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
|
||||
<Label Name="ProgramName" Access="Public" VerticalExpand="True"/>
|
||||
<Label Name="ProgramName" VerticalExpand="True" />
|
||||
<BoxContainer HorizontalExpand="True" SetHeight="28" Margin="0 0 4 4">
|
||||
<BoxContainer HorizontalExpand="True"/>
|
||||
<Button Name="InstallButton" Access="Public" Visible="False" SetWidth="90" HorizontalAlignment="Right" VerticalExpand="True"></Button>
|
||||
<BoxContainer HorizontalExpand="True" />
|
||||
<Button Name="InstallButton" Visible="False" SetWidth="90" HorizontalAlignment="Right" VerticalExpand="True"></Button>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</pda:PdaProgramItem>
|
||||
</ContainerButton>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Robust.Client.AutoGenerated;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
|
@ -12,6 +13,12 @@ public sealed partial class PdaProgramItem : ContainerButton
|
|||
public const string NormalBgColor = "#313138";
|
||||
public const string HoverColor = "#3E6C45";
|
||||
|
||||
private Entity<CartridgeComponent> _cartridge;
|
||||
|
||||
public event Action<EntityUid>? OnProgramItemPressed;
|
||||
public event Action<EntityUid>? OnUninstallButtonPressed;
|
||||
public event Action<EntityUid>? OnInstallButtonPressed;
|
||||
|
||||
private readonly StyleBoxFlat _styleBox = new()
|
||||
{
|
||||
BackgroundColor = Color.FromHex("#25252a"),
|
||||
|
|
@ -23,10 +30,38 @@ public sealed partial class PdaProgramItem : ContainerButton
|
|||
set => _styleBox.BackgroundColor = value;
|
||||
}
|
||||
|
||||
public PdaProgramItem()
|
||||
public PdaProgramItem(Entity<CartridgeComponent> program)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
SetCartridge(program);
|
||||
Panel.PanelOverride = _styleBox;
|
||||
|
||||
OnPressed += OnSelfPressed;
|
||||
InstallButton.OnPressed += OnInstallPressed;
|
||||
}
|
||||
|
||||
private void OnSelfPressed(ButtonEventArgs args)
|
||||
{
|
||||
OnProgramItemPressed?.Invoke(_cartridge);
|
||||
}
|
||||
|
||||
private void OnInstallPressed(ButtonEventArgs args)
|
||||
{
|
||||
switch (_cartridge.Comp.InstallationStatus)
|
||||
{
|
||||
case InstallationStatus.Cartridge:
|
||||
OnInstallButtonPressed?.Invoke(_cartridge.Owner);
|
||||
break;
|
||||
|
||||
case InstallationStatus.Installed:
|
||||
OnUninstallButtonPressed?.Invoke(_cartridge.Owner);
|
||||
break;
|
||||
|
||||
case InstallationStatus.Readonly:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
|
|
@ -35,6 +70,29 @@ public sealed partial class PdaProgramItem : ContainerButton
|
|||
|
||||
if (TryGetStyleProperty<Color>(StylePropertyBgColor, out var bgColor))
|
||||
BackgroundColor = bgColor;
|
||||
}
|
||||
|
||||
public void SetCartridge(Entity<CartridgeComponent> program)
|
||||
{
|
||||
if (program.Comp.Icon is { } icon)
|
||||
Icon.SetFromSpriteSpecifier(icon);
|
||||
|
||||
_cartridge = program;
|
||||
ProgramName.Text = Loc.GetString(program.Comp.ProgramName);
|
||||
|
||||
switch (program.Comp.InstallationStatus)
|
||||
{
|
||||
case InstallationStatus.Cartridge:
|
||||
InstallButton.Visible = true;
|
||||
InstallButton.Text = Loc.GetString("cartridge-bound-user-interface-install-button");
|
||||
break;
|
||||
case InstallationStatus.Installed:
|
||||
InstallButton.Visible = true;
|
||||
InstallButton.Text = Loc.GetString("cartridge-bound-user-interface-uninstall-button");
|
||||
break;
|
||||
case InstallationStatus.Readonly:
|
||||
InstallButton.Visible = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,521 +0,0 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.PDA;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.DeviceNetwork.Events;
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.CartridgeLoader;
|
||||
|
||||
public sealed partial class CartridgeLoaderSystem : SharedCartridgeLoaderSystem
|
||||
{
|
||||
[Dependency] private ContainerSystem _containerSystem = default!;
|
||||
[Dependency] private UserInterfaceSystem _userInterfaceSystem = default!;
|
||||
[Dependency] private PdaSystem _pda = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, MapInitEvent>(OnMapInit);
|
||||
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, AfterInteractEvent>(OnUsed);
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, CartridgeLoaderUiMessage>(OnLoaderUiMessage);
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, CartridgeUiMessage>(OnUiMessage);
|
||||
}
|
||||
|
||||
public IReadOnlyList<EntityUid> GetInstalled(EntityUid uid, ContainerManagerComponent? comp = null)
|
||||
{
|
||||
if (_containerSystem.TryGetContainer(uid, InstalledContainerId, out var container, comp))
|
||||
return container.ContainedEntities;
|
||||
|
||||
return Array.Empty<EntityUid>();
|
||||
}
|
||||
|
||||
public bool TryGetProgram<T>(
|
||||
EntityUid uid,
|
||||
[NotNullWhen(true)] out EntityUid? programUid,
|
||||
[NotNullWhen(true)] out T? program,
|
||||
bool installedOnly = false,
|
||||
CartridgeLoaderComponent? loader = null,
|
||||
ContainerManagerComponent? containerManager = null) where T : IComponent
|
||||
{
|
||||
program = default;
|
||||
programUid = null;
|
||||
|
||||
if (!_containerSystem.TryGetContainer(uid, InstalledContainerId, out var container, containerManager))
|
||||
return false;
|
||||
|
||||
foreach (var prog in container.ContainedEntities)
|
||||
{
|
||||
if (!TryComp(prog, out program))
|
||||
continue;
|
||||
|
||||
programUid = prog;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (installedOnly)
|
||||
return false;
|
||||
|
||||
if (!Resolve(uid, ref loader) || !TryComp(loader.CartridgeSlot.Item, out program))
|
||||
return false;
|
||||
|
||||
programUid = loader.CartridgeSlot.Item;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetProgram<T>(
|
||||
EntityUid uid,
|
||||
[NotNullWhen(true)] out EntityUid? programUid,
|
||||
bool installedOnly = false,
|
||||
CartridgeLoaderComponent? loader = null,
|
||||
ContainerManagerComponent? containerManager = null) where T : IComponent
|
||||
{
|
||||
return TryGetProgram<T>(uid, out programUid, out _, installedOnly, loader, containerManager);
|
||||
}
|
||||
|
||||
public bool HasProgram<T>(
|
||||
EntityUid uid,
|
||||
bool installedOnly = false,
|
||||
CartridgeLoaderComponent? loader = null,
|
||||
ContainerManagerComponent? containerManager = null) where T : IComponent
|
||||
{
|
||||
return TryGetProgram<T>(uid, out _, out _, installedOnly, loader, containerManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the cartridge loaders ui state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Because the cartridge loader integrates with the ui of the entity using it, the entities ui state needs to inherit from <see cref="CartridgeLoaderUiState"/>
|
||||
/// and use this method to update its state so the cartridge loaders state can be added to it.
|
||||
/// </remarks>
|
||||
/// <seealso cref="PDA.PdaSystem.UpdatePdaUserInterface"/>
|
||||
public void UpdateUiState(EntityUid loaderUid, ICommonSession? session, CartridgeLoaderComponent? loader)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return;
|
||||
|
||||
if (!_userInterfaceSystem.HasUi(loaderUid, loader.UiKey))
|
||||
return;
|
||||
|
||||
var programs = GetAvailablePrograms(loaderUid, loader);
|
||||
var state = new CartridgeLoaderUiState(programs, GetNetEntity(loader.ActiveProgram));
|
||||
_userInterfaceSystem.SetUiState(loaderUid, loader.UiKey, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the programs ui state
|
||||
/// </summary>
|
||||
/// <param name="loaderUid">The cartridge loaders entity uid</param>
|
||||
/// <param name="state">The programs ui state. Programs should use their own ui state class inheriting from <see cref="BoundUserInterfaceState"/></param>
|
||||
/// <param name="session">The players session</param>
|
||||
/// <param name="loader">The cartridge loader component</param>
|
||||
/// <remarks>
|
||||
/// This method is called "UpdateCartridgeUiState" but cartridges and a programs are the same. A cartridge is just a program as a visible item.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Cartridges.NotekeeperCartridgeSystem.UpdateUiState"/>
|
||||
public void UpdateCartridgeUiState(EntityUid loaderUid, BoundUserInterfaceState state, ICommonSession? session = default!, CartridgeLoaderComponent? loader = default!)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return;
|
||||
|
||||
if (_userInterfaceSystem.HasUi(loaderUid, loader.UiKey))
|
||||
_userInterfaceSystem.SetUiState(loaderUid, loader.UiKey, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all installed programs and the inserted cartridge if it isn't already installed
|
||||
/// </summary>
|
||||
/// <param name="uid">The cartridge loaders uid</param>
|
||||
/// <param name="loader">The cartridge loader component</param>
|
||||
/// <returns>A list of all the available program entity ids</returns>
|
||||
public List<NetEntity> GetAvailablePrograms(EntityUid uid, CartridgeLoaderComponent? loader = default!)
|
||||
{
|
||||
if (!Resolve(uid, ref loader))
|
||||
return new List<NetEntity>();
|
||||
|
||||
var available = GetNetEntityList(GetInstalled(uid));
|
||||
|
||||
if (loader.CartridgeSlot.Item is not { } cartridge)
|
||||
return available;
|
||||
|
||||
// TODO exclude duplicate programs. Or something I dunno I CBF fixing this mess.
|
||||
available.Add(GetNetEntity(cartridge));
|
||||
return available;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs a cartridge by spawning an invisible version of the cartridges prototype into the cartridge loaders program container program container
|
||||
/// </summary>
|
||||
/// <param name="loaderUid">The cartridge loader uid</param>
|
||||
/// <param name="cartridgeUid">The uid of the cartridge to be installed</param>
|
||||
/// <param name="loader">The cartridge loader component</param>
|
||||
/// <returns>Whether installing the cartridge was successful</returns>
|
||||
public bool InstallCartridge(EntityUid loaderUid, EntityUid cartridgeUid, CartridgeLoaderComponent? loader = default!)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return false;
|
||||
|
||||
if (!TryComp(cartridgeUid, out CartridgeComponent? loadedCartridge))
|
||||
return false;
|
||||
|
||||
foreach (var program in GetInstalled(loaderUid))
|
||||
{
|
||||
if (TryComp(program, out CartridgeComponent? installedCartridge) && installedCartridge.ProgramName == loadedCartridge.ProgramName)
|
||||
return false;
|
||||
}
|
||||
|
||||
//This will eventually be replaced by serializing and deserializing the cartridge to copy it when something needs
|
||||
//the data on the cartridge to carry over when installing
|
||||
|
||||
// For anyone stumbling onto this: Do not do this or I will cut you.
|
||||
var prototypeId = Prototype(cartridgeUid)?.ID;
|
||||
return prototypeId != null && InstallProgram(loaderUid, prototypeId, loader: loader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs a program by its prototype
|
||||
/// </summary>
|
||||
/// <param name="loaderUid">The cartridge loader uid</param>
|
||||
/// <param name="prototype">The prototype name</param>
|
||||
/// <param name="deinstallable">Whether the program can be deinstalled or not</param>
|
||||
/// <param name="loader">The cartridge loader component</param>
|
||||
/// <returns>Whether installing the cartridge was successful</returns>
|
||||
public bool InstallProgram(EntityUid loaderUid, string prototype, bool deinstallable = true, CartridgeLoaderComponent? loader = default!)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return false;
|
||||
|
||||
if (!_containerSystem.TryGetContainer(loaderUid, InstalledContainerId, out var container))
|
||||
return false;
|
||||
|
||||
if (container.Count >= loader.DiskSpace)
|
||||
return false;
|
||||
|
||||
var ev = new ProgramInstallationAttempt(loaderUid, prototype);
|
||||
RaiseLocalEvent(ref ev);
|
||||
|
||||
if (ev.Cancelled)
|
||||
return false;
|
||||
|
||||
var installedProgram = Spawn(prototype, new EntityCoordinates(loaderUid, 0, 0));
|
||||
if (!TryComp(installedProgram, out CartridgeComponent? cartridge))
|
||||
return false;
|
||||
|
||||
_containerSystem.Insert(installedProgram, container);
|
||||
|
||||
UpdateCartridgeInstallationStatus(installedProgram, deinstallable ? InstallationStatus.Installed : InstallationStatus.Readonly, cartridge);
|
||||
cartridge.LoaderUid = loaderUid;
|
||||
|
||||
RaiseLocalEvent(installedProgram, new CartridgeAddedEvent(loaderUid));
|
||||
UpdateUserInterfaceState(loaderUid, loader);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uninstalls a program using its uid
|
||||
/// </summary>
|
||||
/// <param name="loaderUid">The cartridge loader uid</param>
|
||||
/// <param name="programUid">The uid of the program to be uninstalled</param>
|
||||
/// <param name="loader">The cartridge loader component</param>
|
||||
/// <returns>Whether uninstalling the program was successful</returns>
|
||||
public bool UninstallProgram(EntityUid loaderUid, EntityUid programUid, CartridgeLoaderComponent? loader = default!)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return false;
|
||||
|
||||
if (!GetInstalled(loaderUid).Contains(programUid))
|
||||
return false;
|
||||
|
||||
if (TryComp(programUid, out CartridgeComponent? cartridge))
|
||||
cartridge.LoaderUid = null;
|
||||
|
||||
if (loader.ActiveProgram == programUid)
|
||||
loader.ActiveProgram = null;
|
||||
|
||||
loader.BackgroundPrograms.Remove(programUid);
|
||||
QueueDel(programUid);
|
||||
UpdateUserInterfaceState(loaderUid, loader);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates a program or cartridge and displays its ui fragment. Deactivates any previously active program.
|
||||
/// </summary>
|
||||
public void ActivateProgram(EntityUid loaderUid, EntityUid programUid, CartridgeLoaderComponent? loader = default!)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return;
|
||||
|
||||
if (!HasProgram(loaderUid, programUid, loader))
|
||||
return;
|
||||
|
||||
if (loader.ActiveProgram.HasValue)
|
||||
DeactivateProgram(loaderUid, programUid, loader);
|
||||
|
||||
if (!loader.BackgroundPrograms.Contains(programUid))
|
||||
RaiseLocalEvent(programUid, new CartridgeActivatedEvent(loaderUid));
|
||||
|
||||
loader.ActiveProgram = programUid;
|
||||
UpdateUserInterfaceState(loaderUid, loader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates the currently active program or cartridge.
|
||||
/// </summary>
|
||||
public void DeactivateProgram(EntityUid loaderUid, EntityUid programUid, CartridgeLoaderComponent? loader = default!)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return;
|
||||
|
||||
if (!HasProgram(loaderUid, programUid, loader) || loader.ActiveProgram != programUid)
|
||||
return;
|
||||
|
||||
if (!loader.BackgroundPrograms.Contains(programUid))
|
||||
RaiseLocalEvent(programUid, new CartridgeDeactivatedEvent(programUid));
|
||||
|
||||
loader.ActiveProgram = default;
|
||||
UpdateUserInterfaceState(loaderUid, loader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the given program as a running in the background. Programs running in the background will receive certain events like device net packets but not ui messages
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Programs wanting to use this functionality will have to provide a way to register and unregister themselves as background programs through their ui fragment.
|
||||
/// </remarks>
|
||||
public void RegisterBackgroundProgram(EntityUid loaderUid, EntityUid cartridgeUid, CartridgeLoaderComponent? loader = default!)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return;
|
||||
|
||||
if (!HasProgram(loaderUid, cartridgeUid, loader))
|
||||
return;
|
||||
|
||||
if (loader.ActiveProgram != cartridgeUid)
|
||||
RaiseLocalEvent(cartridgeUid, new CartridgeActivatedEvent(loaderUid));
|
||||
|
||||
loader.BackgroundPrograms.Add(cartridgeUid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the given program as running in the background
|
||||
/// </summary>
|
||||
public void UnregisterBackgroundProgram(EntityUid loaderUid, EntityUid cartridgeUid, CartridgeLoaderComponent? loader = default!)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return;
|
||||
|
||||
if (!HasProgram(loaderUid, cartridgeUid, loader))
|
||||
return;
|
||||
|
||||
if (loader.ActiveProgram != cartridgeUid)
|
||||
RaiseLocalEvent(cartridgeUid, new CartridgeDeactivatedEvent(loaderUid));
|
||||
|
||||
loader.BackgroundPrograms.Remove(cartridgeUid);
|
||||
}
|
||||
|
||||
public void SendNotification(EntityUid loaderUid, string header, string message, CartridgeLoaderComponent? loader = default!)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return;
|
||||
|
||||
if (!loader.NotificationsEnabled)
|
||||
return;
|
||||
|
||||
var args = new CartridgeLoaderNotificationSentEvent(header, message);
|
||||
RaiseLocalEvent(loaderUid, ref args);
|
||||
}
|
||||
|
||||
protected override void OnItemInserted(EntityUid uid, CartridgeLoaderComponent loader, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != InstalledContainerId && args.Container.ID != loader.CartridgeSlot.ID)
|
||||
return;
|
||||
|
||||
if (TryComp(args.Entity, out CartridgeComponent? cartridge))
|
||||
cartridge.LoaderUid = uid;
|
||||
|
||||
RaiseLocalEvent(args.Entity, new CartridgeAddedEvent(uid));
|
||||
base.OnItemInserted(uid, loader, args);
|
||||
}
|
||||
|
||||
protected override void OnItemRemoved(EntityUid uid, CartridgeLoaderComponent loader, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != InstalledContainerId && args.Container.ID != loader.CartridgeSlot.ID)
|
||||
return;
|
||||
|
||||
var deactivate = loader.BackgroundPrograms.Remove(args.Entity);
|
||||
|
||||
if (loader.ActiveProgram == args.Entity)
|
||||
{
|
||||
loader.ActiveProgram = default;
|
||||
deactivate = true;
|
||||
}
|
||||
|
||||
if (deactivate)
|
||||
RaiseLocalEvent(args.Entity, new CartridgeDeactivatedEvent(uid));
|
||||
|
||||
if (TryComp(args.Entity, out CartridgeComponent? cartridge))
|
||||
cartridge.LoaderUid = null;
|
||||
|
||||
RaiseLocalEvent(args.Entity, new CartridgeRemovedEvent(uid));
|
||||
base.OnItemRemoved(uid, loader, args);
|
||||
|
||||
_pda.UpdatePdaUi(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs programs from the list of preinstalled programs
|
||||
/// </summary>
|
||||
private void OnMapInit(EntityUid uid, CartridgeLoaderComponent component, MapInitEvent args)
|
||||
{
|
||||
// TODO remove this and use container fill.
|
||||
foreach (var prototype in component.PreinstalledPrograms)
|
||||
{
|
||||
InstallProgram(uid, prototype, deinstallable: false);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUsed(EntityUid uid, CartridgeLoaderComponent component, AfterInteractEvent args)
|
||||
{
|
||||
RelayEvent(component, new CartridgeAfterInteractEvent(uid, args));
|
||||
}
|
||||
|
||||
private void OnPacketReceived(EntityUid uid, CartridgeLoaderComponent component, DeviceNetworkPacketEvent args)
|
||||
{
|
||||
RelayEvent(component, new CartridgeDeviceNetPacketEvent(uid, args));
|
||||
}
|
||||
|
||||
private void OnLoaderUiMessage(EntityUid loaderUid, CartridgeLoaderComponent component, CartridgeLoaderUiMessage message)
|
||||
{
|
||||
var cartridge = GetEntity(message.CartridgeUid);
|
||||
|
||||
switch (message.Action)
|
||||
{
|
||||
case CartridgeUiMessageAction.Activate:
|
||||
ActivateProgram(loaderUid, cartridge, component);
|
||||
break;
|
||||
case CartridgeUiMessageAction.Deactivate:
|
||||
DeactivateProgram(loaderUid, cartridge, component);
|
||||
break;
|
||||
case CartridgeUiMessageAction.Install:
|
||||
InstallCartridge(loaderUid, cartridge, component);
|
||||
break;
|
||||
case CartridgeUiMessageAction.Uninstall:
|
||||
UninstallProgram(loaderUid, cartridge, component);
|
||||
break;
|
||||
case CartridgeUiMessageAction.UIReady:
|
||||
if (component.ActiveProgram.HasValue)
|
||||
RaiseLocalEvent(component.ActiveProgram.Value, new CartridgeUiReadyEvent(loaderUid));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Unrecognized UI action passed from cartridge loader ui {message.Action}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relays ui messages meant for cartridges to the currently active cartridge
|
||||
/// </summary>
|
||||
private void OnUiMessage(EntityUid uid, CartridgeLoaderComponent component, CartridgeUiMessage args)
|
||||
{
|
||||
var cartridgeEvent = args.MessageEvent;
|
||||
cartridgeEvent.User = args.Actor;
|
||||
cartridgeEvent.LoaderUid = GetNetEntity(uid);
|
||||
cartridgeEvent.Actor = args.Actor;
|
||||
|
||||
RelayEvent(component, cartridgeEvent, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relays events to the currently active program and and programs running in the background.
|
||||
/// Skips background programs if "skipBackgroundPrograms" is set to true
|
||||
/// </summary>
|
||||
/// <param name="loader">The cartritge loader component</param>
|
||||
/// <param name="args">The event to be relayed</param>
|
||||
/// <param name="skipBackgroundPrograms">Whether to skip relaying the event to programs running in the background</param>
|
||||
private void RelayEvent<TEvent>(CartridgeLoaderComponent loader, TEvent args, bool skipBackgroundPrograms = false) where TEvent : notnull
|
||||
{
|
||||
if (loader.ActiveProgram.HasValue)
|
||||
RaiseLocalEvent(loader.ActiveProgram.Value, args);
|
||||
|
||||
if (skipBackgroundPrograms)
|
||||
return;
|
||||
|
||||
foreach (var program in loader.BackgroundPrograms)
|
||||
{
|
||||
//Prevent programs registered as running in the background receiving events twice if they are active
|
||||
if (loader.ActiveProgram.HasValue && loader.ActiveProgram.Value.Equals(program))
|
||||
continue;
|
||||
|
||||
RaiseLocalEvent(program, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shortcut for updating the loaders user interface state without passing in a subtype of <see cref="CartridgeLoaderUiState"/>
|
||||
/// like the <see cref="PDA.PdaSystem"/> does when updating its ui state
|
||||
/// </summary>
|
||||
/// <seealso cref="PDA.PdaSystem.UpdatePdaUserInterface"/>
|
||||
private void UpdateUserInterfaceState(EntityUid loaderUid, CartridgeLoaderComponent loader)
|
||||
{
|
||||
UpdateUiState(loaderUid, null, loader);
|
||||
}
|
||||
|
||||
private void UpdateCartridgeInstallationStatus(EntityUid cartridgeUid, InstallationStatus installationStatus, CartridgeComponent cartridgeComponent)
|
||||
{
|
||||
cartridgeComponent.InstallationStatus = installationStatus;
|
||||
Dirty(cartridgeUid, cartridgeComponent);
|
||||
}
|
||||
|
||||
private bool HasProgram(EntityUid loader, EntityUid program, CartridgeLoaderComponent component)
|
||||
{
|
||||
return component.CartridgeSlot.Item == program || GetInstalled(loader).Contains(program);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to running programs when the cartridge loader receives a device net package
|
||||
/// </summary>
|
||||
/// <seealso cref="DeviceNetworkPacketEvent"/>
|
||||
public sealed class CartridgeDeviceNetPacketEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Loader;
|
||||
public readonly DeviceNetworkPacketEvent PacketEvent;
|
||||
|
||||
public CartridgeDeviceNetPacketEvent(EntityUid loader, DeviceNetworkPacketEvent packetEvent)
|
||||
{
|
||||
Loader = loader;
|
||||
PacketEvent = packetEvent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to running programs when the cartridge loader receives an after interact event
|
||||
/// </summary>
|
||||
/// <seealso cref="AfterInteractEvent"/>
|
||||
public sealed class CartridgeAfterInteractEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Loader;
|
||||
public readonly AfterInteractEvent InteractEvent;
|
||||
|
||||
public CartridgeAfterInteractEvent(EntityUid loader, AfterInteractEvent interactEvent)
|
||||
{
|
||||
Loader = loader;
|
||||
InteractEvent = interactEvent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on an attempt of program installation.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ProgramInstallationAttempt(EntityUid LoaderUid, string Prototype, bool Cancelled = false);
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Content.Shared.GPS;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class AstroNavCartridgeComponent : Component
|
||||
{
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CrewManifestCartridgeComponent : Component
|
||||
{
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ using Content.Shared.CartridgeLoader.Cartridges;
|
|||
using Content.Shared.CCVar;
|
||||
using Content.Shared.CrewManifest;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
|
|
@ -85,17 +84,17 @@ public sealed partial class CrewManifestCartridgeSystem : EntitySystem
|
|||
{
|
||||
_unsecureViewersAllowed = unsecureViewersAllowed;
|
||||
|
||||
var allCartridgeLoaders = AllEntityQuery<CartridgeLoaderComponent, ContainerManagerComponent>();
|
||||
while (allCartridgeLoaders.MoveNext(out var loaderUid, out var comp, out var cont))
|
||||
var allCartridgeLoaders = AllEntityQuery<CartridgeLoaderComponent>();
|
||||
while (allCartridgeLoaders.MoveNext(out var loaderUid, out var comp))
|
||||
{
|
||||
if (_unsecureViewersAllowed)
|
||||
{
|
||||
_cartridgeLoader.InstallProgram(loaderUid, CartridgePrototypeName, false, comp);
|
||||
_cartridgeLoader.InstallProgram((loaderUid, comp), CartridgePrototypeName, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cartridgeLoader.TryGetProgram<CrewManifestCartridgeComponent>(loaderUid, out var program, true, comp, cont))
|
||||
_cartridgeLoader.UninstallProgram(loaderUid, program.Value, comp);
|
||||
if (_cartridgeLoader.TryGetProgram<CrewManifestCartridgeComponent>((loaderUid, comp)) is { } program)
|
||||
_cartridgeLoader.UninstallProgram((loaderUid, comp), program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ public sealed partial class MedTekCartridgeSystem : EntitySystem
|
|||
|
||||
private void OnCartridgeAdded(Entity<MedTekCartridgeComponent> ent, ref CartridgeAddedEvent args)
|
||||
{
|
||||
var healthAnalyzer = EnsureComp<HealthAnalyzerComponent>(args.Loader);
|
||||
EnsureComp<HealthAnalyzerComponent>(args.Loader);
|
||||
}
|
||||
|
||||
private void OnCartridgeRemoved(Entity<MedTekCartridgeComponent> ent, ref CartridgeRemovedEvent args)
|
||||
{
|
||||
// only remove when the program itself is removed
|
||||
if (!_cartridgeLoaderSystem.HasProgram<MedTekCartridgeComponent>(args.Loader))
|
||||
if (!_cartridgeLoaderSystem.HasProgram<MedTekCartridgeComponent>(args.Loader.AsNullable()))
|
||||
{
|
||||
RemComp<HealthAnalyzerComponent>(args.Loader);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
using Content.Shared.Security;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class WantedListCartridgeComponent : Component
|
||||
{
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.Access.Systems;
|
||||
using Content.Server.AlertLevel;
|
||||
using Content.Server.CartridgeLoader;
|
||||
|
|
@ -202,7 +203,7 @@ namespace Content.Server.PDA
|
|||
if (!TryComp(uid, out CartridgeLoaderComponent? loader))
|
||||
return;
|
||||
|
||||
var programs = _cartridgeLoader.GetAvailablePrograms(uid, loader);
|
||||
var programs = GetNetEntityList(_cartridgeLoader.GetAllPrograms(uid).ToList());
|
||||
var id = CompOrNull<IdCardComponent>(pda.ContainedId);
|
||||
var state = new PdaUpdateState(
|
||||
programs,
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ namespace Content.Shared.CartridgeLoader;
|
|||
/// <summary>
|
||||
/// This is used for defining values used for displaying in the program ui in yaml
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||
[Access(typeof(CartridgeLoaderSystem))]
|
||||
public sealed partial class CartridgeComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? LoaderUid;
|
||||
|
||||
[DataField(required: true)]
|
||||
|
|
@ -19,7 +20,7 @@ public sealed partial class CartridgeComponent : Component
|
|||
[DataField]
|
||||
public SpriteSpecifier? Icon;
|
||||
|
||||
[AutoNetworkedField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public InstallationStatus InstallationStatus = InstallationStatus.Cartridge;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,43 +3,36 @@ using Robust.Shared.GameStates;
|
|||
|
||||
namespace Content.Shared.CartridgeLoader;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(CartridgeLoaderSystem))]
|
||||
public sealed partial class CartridgeLoaderComponent : Component
|
||||
{
|
||||
public const string UnremovableContainerId = "preinstalled-program-container";
|
||||
|
||||
public const string RemovableContainerId = "removable-program-container";
|
||||
|
||||
public const string CartridgeSlotId = "Cartridge-Slot";
|
||||
|
||||
[DataField]
|
||||
public ItemSlot CartridgeSlot = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of programs that come preinstalled with this cartridge loader
|
||||
/// </summary>
|
||||
[DataField("preinstalled")] // TODO remove this and use container fill.
|
||||
public List<string> PreinstalledPrograms = new();
|
||||
|
||||
/// <summary>
|
||||
/// The currently running program that has its ui showing
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? ActiveProgram = default;
|
||||
|
||||
/// <summary>
|
||||
/// The list of programs running in the background, listening to certain events
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public readonly List<EntityUid> BackgroundPrograms = new();
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of programs that can be installed on the cartridge loader entity
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public int DiskSpace = 8;
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether the cartridge loader will play notifications if it supports it at all
|
||||
/// TODO: Add an option for this to the PDA
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool NotificationsEnabled = true;
|
||||
|
||||
[DataField(required: true)]
|
||||
|
|
|
|||
171
Content.Shared/CartridgeLoader/CartridgeLoaderSystem.Programs.cs
Normal file
171
Content.Shared/CartridgeLoader/CartridgeLoaderSystem.Programs.cs
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
using System.Linq;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.CartridgeLoader;
|
||||
|
||||
public sealed partial class CartridgeLoaderSystem
|
||||
{
|
||||
public Entity<T>? TryGetProgram<T>(Entity<CartridgeLoaderComponent?> ent) where T : IComponent
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return null;
|
||||
|
||||
foreach (var prog in GetAllPrograms(ent))
|
||||
{
|
||||
if (!TryComp<T>(prog, out var program))
|
||||
continue;
|
||||
|
||||
return (prog, program);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool HasProgram<T>(Entity<CartridgeLoaderComponent?> ent) where T : IComponent
|
||||
{
|
||||
return TryGetProgram<T>(ent).HasValue;
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetAllPrograms(EntityUid uid)
|
||||
{
|
||||
if (_itemSlotsSystem.GetItemOrNull(uid, CartridgeLoaderComponent.CartridgeSlotId) is { } cartridge)
|
||||
return GetDiskPrograms(uid).Append(cartridge);
|
||||
|
||||
return GetDiskPrograms(uid);
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetDiskPrograms(EntityUid uid)
|
||||
{
|
||||
return GetPreinstalledPrograms(uid).Concat(GetRemovablePrograms(uid));
|
||||
}
|
||||
|
||||
public IReadOnlyList<EntityUid> GetRemovablePrograms(EntityUid uid)
|
||||
{
|
||||
if (_container.TryGetContainer(uid, CartridgeLoaderComponent.RemovableContainerId, out var container))
|
||||
return container.ContainedEntities;
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public IReadOnlyList<EntityUid> GetPreinstalledPrograms(EntityUid uid)
|
||||
{
|
||||
if (_container.TryGetContainer(uid, CartridgeLoaderComponent.UnremovableContainerId, out var container))
|
||||
return container.ContainedEntities;
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public int UsedDiskSpace(EntityUid uid)
|
||||
{
|
||||
var ret = 0;
|
||||
|
||||
if (_container.TryGetContainer(uid, CartridgeLoaderComponent.RemovableContainerId, out var removable))
|
||||
ret += removable.Count;
|
||||
|
||||
if (_container.TryGetContainer(uid, CartridgeLoaderComponent.UnremovableContainerId, out var unremovable))
|
||||
ret += unremovable.Count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private bool HasProgram(EntityUid uid, EntityUid program)
|
||||
{
|
||||
return GetAllPrograms(uid).Contains(program);
|
||||
}
|
||||
|
||||
public void ActivateProgram(Entity<CartridgeLoaderComponent> ent, EntityUid program)
|
||||
{
|
||||
if (!HasProgram(ent, program))
|
||||
return;
|
||||
|
||||
if (ent.Comp.ActiveProgram is { } active)
|
||||
DeactivateProgram(ent, active);
|
||||
|
||||
var evt = new CartridgeActivatedEvent(ent);
|
||||
RaiseLocalEvent(program, ref evt);
|
||||
ent.Comp.ActiveProgram = program;
|
||||
Dirty(ent);
|
||||
UpdateUiState(ent.AsNullable());
|
||||
}
|
||||
|
||||
public void DeactivateProgram(Entity<CartridgeLoaderComponent> ent, EntityUid program)
|
||||
{
|
||||
if (!HasProgram(ent, program) || ent.Comp.ActiveProgram != program)
|
||||
return;
|
||||
|
||||
var evt = new CartridgeDeactivatedEvent(ent);
|
||||
RaiseLocalEvent(program, ref evt);
|
||||
ent.Comp.ActiveProgram = null;
|
||||
Dirty(ent);
|
||||
UpdateUiState(ent.AsNullable());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs a program by its prototype.
|
||||
/// </summary>
|
||||
/// <param name="ent">The cartridge loader.</param>
|
||||
/// <param name="prototype">The prototype.</param>
|
||||
/// <param name="deinstallable">Whether the program can be deinstalled or not.</param>
|
||||
/// <returns>Whether installing the cartridge was successful.</returns>
|
||||
public bool InstallProgram(Entity<CartridgeLoaderComponent> ent, EntProtoId prototype, bool deinstallable = true)
|
||||
{
|
||||
if (UsedDiskSpace(ent) >= ent.Comp.DiskSpace)
|
||||
return false;
|
||||
|
||||
var ev = new ProgramInstallationAttempt(ent, prototype);
|
||||
RaiseLocalEvent(ref ev);
|
||||
|
||||
if (ev.Cancelled)
|
||||
return false;
|
||||
|
||||
return PredictedTrySpawnInContainer(prototype,
|
||||
ent,
|
||||
deinstallable ? CartridgeLoaderComponent.RemovableContainerId : CartridgeLoaderComponent.UnremovableContainerId,
|
||||
out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uninstalls a program using its uid.
|
||||
/// </summary>
|
||||
/// <param name="ent">The cartridge loader uid.</param>
|
||||
/// <param name="program">The uid of the program to be uninstalled.</param>
|
||||
/// <returns>Whether uninstalling the program was successful.</returns>
|
||||
public bool UninstallProgram(Entity<CartridgeLoaderComponent> ent, EntityUid program)
|
||||
{
|
||||
if (!GetDiskPrograms(ent).Contains(program))
|
||||
return false;
|
||||
|
||||
PredictedQueueDel(program);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs a cartridge by spawning an invisible version of the cartridges prototype into the cartridge loaders program container.
|
||||
/// </summary>
|
||||
/// <param name="ent">The cartridge loader.</param>
|
||||
/// <param name="cartridge">The uid of the cartridge to be installed.</param>
|
||||
/// <returns>Whether installing the cartridge was successful.</returns>
|
||||
public bool InstallCartridge(Entity<CartridgeLoaderComponent> ent, EntityUid cartridge)
|
||||
{
|
||||
if (!HasComp<CartridgeComponent>(cartridge))
|
||||
return false;
|
||||
|
||||
if (MetaData(cartridge).EntityPrototype is not { } cartridgeProto)
|
||||
return false;
|
||||
|
||||
foreach (var program in GetDiskPrograms(ent))
|
||||
{
|
||||
if (MetaData(program).EntityPrototype is { } programProto && programProto == cartridgeProto)
|
||||
return false;
|
||||
}
|
||||
|
||||
return InstallProgram(ent, cartridgeProto);
|
||||
}
|
||||
|
||||
private void UpdateCartridgeInstallationStatus(Entity<CartridgeComponent> ent, InstallationStatus installationStatus)
|
||||
{
|
||||
ent.Comp.InstallationStatus = installationStatus;
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
using Content.Shared.DeviceNetwork.Events;
|
||||
using Content.Shared.Interaction;
|
||||
|
||||
namespace Content.Shared.CartridgeLoader;
|
||||
|
||||
public sealed partial class CartridgeLoaderSystem
|
||||
{
|
||||
private void InitializeRelay()
|
||||
{
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, AfterInteractEvent>(RelayEvent);
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, InteractUsingEvent>(RelayEvent);
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, DeviceNetworkPacketEvent>(RelayEvent);
|
||||
}
|
||||
|
||||
private void RefRelayEvent<T>(EntityUid uid, CartridgeLoaderComponent component, ref T args) where T : struct
|
||||
{
|
||||
RefRelayEvent((uid, component), ref args);
|
||||
}
|
||||
|
||||
private void RelayEvent<T>(EntityUid uid, CartridgeLoaderComponent component, T args) where T : class
|
||||
{
|
||||
RelayEvent((uid, component), args);
|
||||
}
|
||||
|
||||
public void RefRelayEvent<T>(Entity<CartridgeLoaderComponent> ent, ref T args, bool foregroundOnly = false) where T : struct
|
||||
{
|
||||
var ev = new CartridgeRelayedEvent<T>(ent, args);
|
||||
if (foregroundOnly)
|
||||
{
|
||||
if (ent.Comp.ActiveProgram is { } foreground)
|
||||
RaiseLocalEvent(foreground, ref ev);
|
||||
args = ev.Args;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var program in GetAllPrograms(ent))
|
||||
{
|
||||
RaiseLocalEvent(program, ref ev);
|
||||
}
|
||||
args = ev.Args;
|
||||
}
|
||||
|
||||
public void RelayEvent<T>(Entity<CartridgeLoaderComponent> ent, T args, bool foregroundOnly = false) where T : class
|
||||
{
|
||||
var ev = new CartridgeRelayedEvent<T>(ent, args);
|
||||
if (foregroundOnly)
|
||||
{
|
||||
if (ent.Comp.ActiveProgram is { } foreground)
|
||||
RaiseLocalEvent(foreground, ref ev);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var program in GetAllPrograms(ent))
|
||||
{
|
||||
RaiseLocalEvent(program, ref ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event wrapper for relayed events.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct CartridgeRelayedEvent<TEvent>(Entity<CartridgeLoaderComponent> Loader, TEvent Args);
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
using System.Linq;
|
||||
|
||||
namespace Content.Shared.CartridgeLoader;
|
||||
|
||||
public sealed partial class CartridgeLoaderSystem
|
||||
{
|
||||
private void OnLoaderUiMessage(Entity<CartridgeLoaderComponent> ent, ref CartridgeLoaderUiMessage message)
|
||||
{
|
||||
var cartridge = GetEntity(message.CartridgeUid);
|
||||
|
||||
switch (message.Action)
|
||||
{
|
||||
case CartridgeUiMessageAction.Activate:
|
||||
ActivateProgram(ent, cartridge);
|
||||
break;
|
||||
case CartridgeUiMessageAction.Deactivate:
|
||||
DeactivateProgram(ent, cartridge);
|
||||
break;
|
||||
case CartridgeUiMessageAction.Install:
|
||||
InstallCartridge(ent, cartridge);
|
||||
break;
|
||||
case CartridgeUiMessageAction.Uninstall:
|
||||
UninstallProgram(ent, cartridge);
|
||||
break;
|
||||
case CartridgeUiMessageAction.UIReady:
|
||||
if (ent.Comp.ActiveProgram is { } foreground)
|
||||
{
|
||||
var evt = new CartridgeUiReadyEvent(ent);
|
||||
RaiseLocalEvent(foreground, ref evt);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Unrecognized UI action passed from cartridge loader ui {message.Action}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relays ui messages meant for cartridges to the currently active cartridge.
|
||||
/// </summary>
|
||||
private void OnUiMessage(Entity<CartridgeLoaderComponent> ent, ref CartridgeUiMessage args)
|
||||
{
|
||||
var cartridgeEvent = args.MessageEvent;
|
||||
cartridgeEvent.User = args.Actor;
|
||||
cartridgeEvent.LoaderUid = GetNetEntity(ent);
|
||||
cartridgeEvent.Actor = args.Actor;
|
||||
|
||||
if (ent.Comp.ActiveProgram is { } foreground)
|
||||
RaiseLocalEvent(foreground, cartridgeEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the programs ui state.
|
||||
/// </summary>
|
||||
/// <param name="loaderUid">The cartridge loaders entity uid.</param>
|
||||
/// <param name="state">The programs ui state. Programs should use their own ui state class inheriting from <see cref="BoundUserInterfaceState"/>.</param>
|
||||
/// <param name="loader">The cartridge loader component.</param>
|
||||
/// <remarks>
|
||||
/// This method is called "UpdateCartridgeUiState" but cartridges and a programs are the same. A cartridge is just a program as a visible item.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Cartridges.NotekeeperCartridgeSystem.UpdateUiState"/>
|
||||
public void UpdateCartridgeUiState(EntityUid loaderUid, BoundUserInterfaceState state, CartridgeLoaderComponent? loader = null)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return;
|
||||
|
||||
if (_userInterface.HasUi(loaderUid, loader.UiKey))
|
||||
_userInterface.SetUiState(loaderUid, loader.UiKey, state);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates the cartridge loaders ui state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Because the cartridge loader integrates with the ui of the entity using it, the entities ui state needs to inherit from <see cref="CartridgeLoaderUiState"/>
|
||||
/// and use this method to update its state so the cartridge loaders state can be added to it.
|
||||
/// </remarks>
|
||||
/// <seealso cref="PDA.PdaSystem.UpdatePdaUserInterface"/>
|
||||
public void UpdateUiState(Entity<CartridgeLoaderComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return;
|
||||
|
||||
if (!_userInterface.HasUi(ent.Owner, ent.Comp.UiKey))
|
||||
return;
|
||||
|
||||
var programs = GetNetEntityList(GetAllPrograms(ent).ToList());
|
||||
var state = new CartridgeLoaderUiState(programs, GetNetEntity(ent.Comp.ActiveProgram));
|
||||
_userInterface.SetUiState(ent.Owner, ent.Comp.UiKey, state);
|
||||
}
|
||||
}
|
||||
185
Content.Shared/CartridgeLoader/CartridgeLoaderSystem.cs
Normal file
185
Content.Shared/CartridgeLoader/CartridgeLoaderSystem.cs
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
using Content.Shared.Containers.ItemSlots;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.CartridgeLoader;
|
||||
|
||||
public sealed partial class CartridgeLoaderSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private IGameTiming _timing = default!;
|
||||
[Dependency] private ItemSlotsSystem _itemSlotsSystem = default!;
|
||||
[Dependency] private SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private SharedContainerSystem _container = default!;
|
||||
[Dependency] private SharedUserInterfaceSystem _userInterface = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
InitializeRelay();
|
||||
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, ComponentRemove>(OnComponentRemove);
|
||||
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, EntInsertedIntoContainerMessage>(OnItemInserted);
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, EntRemovedFromContainerMessage>(OnItemRemoved);
|
||||
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, CartridgeLoaderUiMessage>(OnLoaderUiMessage);
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, CartridgeUiMessage>(OnUiMessage);
|
||||
|
||||
SubscribeLocalEvent<CartridgeComponent, AfterAutoHandleStateEvent>(OnCartridgeState);
|
||||
}
|
||||
|
||||
private void OnComponentInit(Entity<CartridgeLoaderComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
_itemSlotsSystem.AddItemSlot(ent, CartridgeLoaderComponent.CartridgeSlotId, ent.Comp.CartridgeSlot);
|
||||
_container.EnsureContainer<Container>(ent, CartridgeLoaderComponent.RemovableContainerId);
|
||||
_container.EnsureContainer<Container>(ent, CartridgeLoaderComponent.UnremovableContainerId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks installed program entities for deletion when the component gets removed
|
||||
/// </summary>
|
||||
private void OnComponentRemove(Entity<CartridgeLoaderComponent> ent, ref ComponentRemove args)
|
||||
{
|
||||
_itemSlotsSystem.RemoveItemSlot(ent, ent.Comp.CartridgeSlot);
|
||||
if (_container.TryGetContainer(ent, CartridgeLoaderComponent.RemovableContainerId, out var removable))
|
||||
_container.ShutdownContainer(removable);
|
||||
if (_container.TryGetContainer(ent, CartridgeLoaderComponent.UnremovableContainerId, out var unremovable))
|
||||
_container.ShutdownContainer(unremovable);
|
||||
}
|
||||
|
||||
private void OnItemInserted(Entity<CartridgeLoaderComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
if (args.Container.ID != CartridgeLoaderComponent.RemovableContainerId
|
||||
&& args.Container.ID != CartridgeLoaderComponent.UnremovableContainerId
|
||||
&& args.Container.ID != CartridgeLoaderComponent.CartridgeSlotId
|
||||
) return;
|
||||
|
||||
if (TryComp<CartridgeComponent>(args.Entity, out var cartridge))
|
||||
{
|
||||
cartridge.LoaderUid = ent;
|
||||
Dirty(args.Entity, cartridge);
|
||||
|
||||
if (args.Container.ID == CartridgeLoaderComponent.RemovableContainerId)
|
||||
UpdateCartridgeInstallationStatus((args.Entity, cartridge), InstallationStatus.Installed);
|
||||
else if (args.Container.ID == CartridgeLoaderComponent.UnremovableContainerId)
|
||||
UpdateCartridgeInstallationStatus((args.Entity, cartridge), InstallationStatus.Readonly);
|
||||
else
|
||||
UpdateCartridgeInstallationStatus((args.Entity, cartridge), InstallationStatus.Cartridge);
|
||||
}
|
||||
|
||||
var evt = new CartridgeAddedEvent(ent);
|
||||
RaiseLocalEvent(args.Entity, ref evt);
|
||||
UpdateUiState(ent.AsNullable());
|
||||
UpdateAppearanceData(ent);
|
||||
}
|
||||
|
||||
private void OnItemRemoved(Entity<CartridgeLoaderComponent> ent, ref EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
if (args.Container.ID != CartridgeLoaderComponent.RemovableContainerId
|
||||
&& args.Container.ID != CartridgeLoaderComponent.UnremovableContainerId
|
||||
&& args.Container.ID != CartridgeLoaderComponent.CartridgeSlotId
|
||||
) return;
|
||||
|
||||
if (ent.Comp.ActiveProgram == args.Entity)
|
||||
{
|
||||
ent.Comp.ActiveProgram = null;
|
||||
Dirty(ent);
|
||||
var deactivated = new CartridgeDeactivatedEvent(ent);
|
||||
RaiseLocalEvent(args.Entity, ref deactivated);
|
||||
}
|
||||
|
||||
if (TryComp<CartridgeComponent>(args.Entity, out var cartridge))
|
||||
{
|
||||
cartridge.LoaderUid = null;
|
||||
Dirty(args.Entity, cartridge);
|
||||
}
|
||||
|
||||
var removed = new CartridgeRemovedEvent(ent);
|
||||
RaiseLocalEvent(args.Entity, ref removed);
|
||||
UpdateUiState(ent.AsNullable());
|
||||
UpdateAppearanceData(ent);
|
||||
}
|
||||
|
||||
private void UpdateAppearanceData(Entity<CartridgeLoaderComponent> ent)
|
||||
{
|
||||
_appearanceSystem.SetData(ent.Owner, CartridgeLoaderVisuals.CartridgeInserted, ent.Comp.CartridgeSlot.HasItem);
|
||||
}
|
||||
|
||||
public void SendNotification(EntityUid loaderUid, string header, string message, CartridgeLoaderComponent? loader = default)
|
||||
{
|
||||
if (!Resolve(loaderUid, ref loader))
|
||||
return;
|
||||
|
||||
if (!loader.NotificationsEnabled)
|
||||
return;
|
||||
|
||||
var args = new CartridgeLoaderNotificationSentEvent(header, message);
|
||||
RaiseLocalEvent(loaderUid, ref args);
|
||||
}
|
||||
|
||||
private void OnCartridgeState(Entity<CartridgeComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (ent.Comp.LoaderUid is not { } loader)
|
||||
return;
|
||||
|
||||
UpdateUiState(loader);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to program / cartridge entities when they get inserted or installed.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct CartridgeAddedEvent(Entity<CartridgeLoaderComponent> Loader);
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to cartridge entities when they get ejected.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct CartridgeRemovedEvent(Entity<CartridgeLoaderComponent> Loader);
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to program / cartridge entities when they get activated.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Don't update the programs ui state in this events listener.
|
||||
/// </remarks>
|
||||
[ByRefEvent]
|
||||
public record struct CartridgeActivatedEvent(Entity<CartridgeLoaderComponent> Loader);
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to program / cartridge entities when they get deactivated.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct CartridgeDeactivatedEvent(Entity<CartridgeLoaderComponent> Loader);
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to program / cartridge entities when the ui is ready to be updated by the cartridge.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used for the initial ui state update because updating the ui in the activate event doesn't work.
|
||||
/// </remarks>
|
||||
[ByRefEvent]
|
||||
public record struct CartridgeUiReadyEvent(Entity<CartridgeLoaderComponent> Loader);
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent by the cartridge loader system to the cartridge loader entity so another system.
|
||||
/// can handle displaying the notification
|
||||
/// </summary>
|
||||
/// <param name="Message">The message to be displayed.</param>
|
||||
[ByRefEvent]
|
||||
public record struct CartridgeLoaderNotificationSentEvent(string Header, string Message);
|
||||
|
||||
/// <summary>
|
||||
/// Raised on an attempt of program installation.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ProgramInstallationAttempt(EntityUid LoaderUid, string Prototype, bool Cancelled = false);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class AstroNavCartridgeComponent : Component;
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Content.Shared.GPS.Components;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
public sealed partial class AstroNavCartridgeSystem : EntitySystem
|
||||
{
|
||||
|
|
@ -24,7 +22,7 @@ public sealed partial class AstroNavCartridgeSystem : EntitySystem
|
|||
private void OnCartridgeRemoved(Entity<AstroNavCartridgeComponent> ent, ref CartridgeRemovedEvent args)
|
||||
{
|
||||
// only remove when the program itself is removed
|
||||
if (!_cartridgeLoaderSystem.HasProgram<AstroNavCartridgeComponent>(args.Loader))
|
||||
if (!_cartridgeLoaderSystem.HasProgram<AstroNavCartridgeComponent>(args.Loader.AsNullable()))
|
||||
{
|
||||
RemComp<HandheldGPSComponent>(args.Loader);
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class CrewManifestCartridgeComponent : Component;
|
||||
|
|
@ -1,31 +1,31 @@
|
|||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Content.Shared.Paper;
|
||||
using Content.Shared.Paper;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent, Access(typeof(LogProbeCartridgeSystem))]
|
||||
[AutoGenerateComponentPause]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||
[Access(typeof(LogProbeCartridgeSystem))]
|
||||
public sealed partial class LogProbeCartridgeComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the scanned entity, sent to clients when they open the UI.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public string EntityName = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The list of pulled access logs
|
||||
/// </summary>
|
||||
[DataField, ViewVariables]
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<PulledAccessLog> PulledAccessLogs = new();
|
||||
|
||||
/// <summary>
|
||||
/// The sound to make when we scan something with access
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public SoundSpecifier SoundScan = new SoundPathSpecifier("/Audio/Machines/scan_finish.ogg", AudioParams.Default.WithVariation(0.25f));
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -46,6 +46,6 @@ public sealed partial class LogProbeCartridgeComponent : Component
|
|||
/// <summary>
|
||||
/// When anyone is allowed to spawn another printout.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField]
|
||||
public TimeSpan NextPrintAllowed = TimeSpan.Zero;
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Labels.EntitySystems;
|
||||
using Content.Shared.Paper;
|
||||
using Content.Shared.Popups;
|
||||
|
|
@ -11,7 +10,7 @@ using Robust.Shared.Audio.Systems;
|
|||
using Robust.Shared.Timing;
|
||||
using System.Text;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
public sealed partial class LogProbeCartridgeSystem : EntitySystem
|
||||
{
|
||||
|
|
@ -30,27 +29,24 @@ public sealed partial class LogProbeCartridgeSystem : EntitySystem
|
|||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<LogProbeCartridgeComponent, CartridgeUiReadyEvent>(OnUiReady);
|
||||
SubscribeLocalEvent<LogProbeCartridgeComponent, CartridgeAfterInteractEvent>(AfterInteract);
|
||||
SubscribeLocalEvent<LogProbeCartridgeComponent, CartridgeRelayedEvent<AfterInteractEvent>>(AfterInteract);
|
||||
SubscribeLocalEvent<LogProbeCartridgeComponent, CartridgeMessageEvent>(OnMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="CartridgeAfterInteractEvent" /> gets relayed to this system if the cartridge loader is running
|
||||
/// the LogProbe program and someone clicks on something with it. <br/>
|
||||
/// <br/>
|
||||
/// Updates the program's list of logs with those from the device.
|
||||
/// </summary>
|
||||
private void AfterInteract(Entity<LogProbeCartridgeComponent> ent, ref CartridgeAfterInteractEvent args)
|
||||
private void AfterInteract(Entity<LogProbeCartridgeComponent> ent, ref CartridgeRelayedEvent<AfterInteractEvent> args)
|
||||
{
|
||||
if (args.InteractEvent.Handled || !args.InteractEvent.CanReach || args.InteractEvent.Target is not { } target)
|
||||
if (args.Args.Handled || !args.Args.CanReach || args.Args.Target is not { } target)
|
||||
return;
|
||||
|
||||
if (!TryComp(target, out AccessReaderComponent? accessReaderComponent))
|
||||
return;
|
||||
|
||||
//Play scanning sound with slightly randomized pitch
|
||||
_audio.PlayEntity(ent.Comp.SoundScan, args.InteractEvent.User, target);
|
||||
_popup.PopupCursor(Loc.GetString("log-probe-scan", ("device", target)), args.InteractEvent.User);
|
||||
_audio.PlayPredicted(ent.Comp.SoundScan, target, args.Args.User);
|
||||
_popup.PopupPredictedCursor(Loc.GetString("log-probe-scan", ("device", target)), args.Args.User);
|
||||
|
||||
ent.Comp.EntityName = Name(target);
|
||||
ent.Comp.PulledAccessLogs.Clear();
|
||||
|
|
@ -68,6 +64,7 @@ public sealed partial class LogProbeCartridgeSystem : EntitySystem
|
|||
// Reverse the list so the oldest is at the bottom
|
||||
ent.Comp.PulledAccessLogs.Reverse();
|
||||
|
||||
Dirty(ent);
|
||||
UpdateUiState(ent, args.Loader);
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +92,7 @@ public sealed partial class LogProbeCartridgeSystem : EntitySystem
|
|||
|
||||
ent.Comp.NextPrintAllowed = _timing.CurTime + ent.Comp.PrintCooldown;
|
||||
|
||||
var paper = Spawn(ent.Comp.PaperPrototype, _transform.GetMapCoordinates(user));
|
||||
var paper = EntityManager.PredictedSpawn(ent.Comp.PaperPrototype, _transform.GetMapCoordinates(user));
|
||||
_label.Label(paper, ent.Comp.EntityName); // label it for easy identification
|
||||
|
||||
_audio.PlayEntity(ent.Comp.PrintSound, user, paper);
|
||||
|
|
@ -117,6 +114,7 @@ public sealed partial class LogProbeCartridgeSystem : EntitySystem
|
|||
_paper.SetContent((paper, paperComp), builder.ToString());
|
||||
|
||||
_adminLogger.Add(LogType.EntitySpawn, LogImpact.Low, $"{ToPrettyString(user):user} printed out LogProbe logs ({paper}) of {ent.Comp.EntityName}");
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void UpdateUiState(Entity<LogProbeCartridgeComponent> ent, EntityUid loaderUid)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class MedTekCartridgeComponent : Component
|
||||
{
|
||||
}
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class MedTekCartridgeComponent : Component;
|
||||
|
|
@ -1,29 +1,29 @@
|
|||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
/// <summary>
|
||||
/// Component that indicates a PDA cartridge as containing the NanoTask program
|
||||
/// </summary>
|
||||
[RegisterComponent, AutoGenerateComponentPause]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause, AutoGenerateComponentState]
|
||||
public sealed partial class NanoTaskCartridgeComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of tasks
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<NanoTaskItemAndId> Tasks = new();
|
||||
|
||||
/// <summary>
|
||||
/// counter for generating task IDs
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public int Counter = 1;
|
||||
|
||||
/// <summary>
|
||||
/// When the user can print again
|
||||
/// </summary>
|
||||
[DataField, AutoPausedField]
|
||||
[DataField, AutoNetworkedField, AutoPausedField]
|
||||
public TimeSpan NextPrintAllowedAfter = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -32,11 +32,3 @@ public sealed partial class NanoTaskCartridgeComponent : Component
|
|||
[DataField]
|
||||
public TimeSpan PrintDelay = TimeSpan.FromSeconds(5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Component attached to the PDA a NanoTask cartridge is inserted into for interaction handling
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class NanoTaskInteractionComponent : Component
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,14 @@
|
|||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Paper;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
/// <summary>
|
||||
/// Server-side class implementing the core UI logic of NanoTask
|
||||
/// </summary>
|
||||
public sealed partial class NanoTaskCartridgeSystem : SharedNanoTaskCartridgeSystem
|
||||
public sealed partial class NanoTaskCartridgeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private CartridgeLoaderSystem _cartridgeLoader = default!;
|
||||
[Dependency] private IGameTiming _timing = default!;
|
||||
|
|
@ -27,36 +22,21 @@ public sealed partial class NanoTaskCartridgeSystem : SharedNanoTaskCartridgeSys
|
|||
|
||||
SubscribeLocalEvent<NanoTaskCartridgeComponent, CartridgeMessageEvent>(OnUiMessage);
|
||||
SubscribeLocalEvent<NanoTaskCartridgeComponent, CartridgeUiReadyEvent>(OnUiReady);
|
||||
|
||||
SubscribeLocalEvent<NanoTaskCartridgeComponent, CartridgeRemovedEvent>(OnCartridgeRemoved);
|
||||
|
||||
SubscribeLocalEvent<NanoTaskInteractionComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<NanoTaskCartridgeComponent, CartridgeRelayedEvent<InteractUsingEvent>>(OnInteractUsing);
|
||||
}
|
||||
|
||||
private void OnCartridgeRemoved(Entity<NanoTaskCartridgeComponent> ent, ref CartridgeRemovedEvent args)
|
||||
private void OnInteractUsing(Entity<NanoTaskCartridgeComponent> ent, ref CartridgeRelayedEvent<InteractUsingEvent> args)
|
||||
{
|
||||
if (!_cartridgeLoader.HasProgram<NanoTaskCartridgeComponent>(args.Loader))
|
||||
{
|
||||
RemComp<NanoTaskInteractionComponent>(args.Loader);
|
||||
}
|
||||
}
|
||||
if (!TryComp<NanoTaskPrintedComponent>(args.Args.Used, out var printed))
|
||||
return;
|
||||
|
||||
private void OnInteractUsing(Entity<NanoTaskInteractionComponent> ent, ref InteractUsingEvent args)
|
||||
{
|
||||
if (!_cartridgeLoader.TryGetProgram<NanoTaskCartridgeComponent>(ent.Owner, out var uid, out var program))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!TryComp<NanoTaskPrintedComponent>(args.Used, out var printed))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (printed.Task is NanoTaskItem item)
|
||||
{
|
||||
program.Tasks.Add(new(program.Counter++, printed.Task));
|
||||
args.Handled = true;
|
||||
Del(args.Used);
|
||||
UpdateUiState(new Entity<NanoTaskCartridgeComponent>(uid.Value, program), ent.Owner);
|
||||
ent.Comp.Tasks.Add(new(ent.Comp.Counter++, printed.Task));
|
||||
args.Args.Handled = true;
|
||||
PredictedQueueDel(args.Args.Used);
|
||||
Dirty(ent);
|
||||
UpdateUiState(ent, args.Loader);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,11 +85,14 @@ public sealed partial class NanoTaskCartridgeSystem : SharedNanoTaskCartridgeSys
|
|||
switch (message.Payload)
|
||||
{
|
||||
case NanoTaskAddTask task:
|
||||
{
|
||||
if (!task.Item.Validate())
|
||||
return;
|
||||
|
||||
ent.Comp.Tasks.Add(new(ent.Comp.Counter++, task.Item));
|
||||
Dirty(ent);
|
||||
break;
|
||||
}
|
||||
case NanoTaskUpdateTask task:
|
||||
{
|
||||
if (!task.Item.Data.Validate())
|
||||
|
|
@ -118,23 +101,30 @@ public sealed partial class NanoTaskCartridgeSystem : SharedNanoTaskCartridgeSys
|
|||
var idx = ent.Comp.Tasks.FindIndex(t => t.Id == task.Item.Id);
|
||||
if (idx != -1)
|
||||
ent.Comp.Tasks[idx] = task.Item;
|
||||
|
||||
Dirty(ent);
|
||||
break;
|
||||
}
|
||||
case NanoTaskDeleteTask task:
|
||||
{
|
||||
ent.Comp.Tasks.RemoveAll(t => t.Id == task.Id);
|
||||
Dirty(ent);
|
||||
break;
|
||||
}
|
||||
case NanoTaskPrintTask task:
|
||||
{
|
||||
if (!task.Item.Validate())
|
||||
return;
|
||||
|
||||
if (_timing.CurTime < ent.Comp.NextPrintAllowedAfter)
|
||||
return;
|
||||
|
||||
ent.Comp.NextPrintAllowedAfter = _timing.CurTime + ent.Comp.PrintDelay;
|
||||
var printed = Spawn("PaperNanoTaskItem", Transform(message.Actor).Coordinates);
|
||||
var printed = PredictedSpawnAtPosition("PaperNanoTaskItem", Transform(message.Actor).Coordinates);
|
||||
_hands.PickupOrDrop(message.Actor, printed);
|
||||
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Machines/printer.ogg"), ent.Owner);
|
||||
SetupPrintedTask(printed, task.Item);
|
||||
Dirty(ent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -142,7 +132,6 @@ public sealed partial class NanoTaskCartridgeSystem : SharedNanoTaskCartridgeSys
|
|||
UpdateUiState(ent, GetEntity(args.LoaderUid));
|
||||
}
|
||||
|
||||
|
||||
private void UpdateUiState(Entity<NanoTaskCartridgeComponent> ent, EntityUid loaderUid)
|
||||
{
|
||||
var state = new NanoTaskUiState(ent.Comp.Tasks);
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
/// <summary>
|
||||
/// Component attached to a piece of paper to indicate that it was printed from NanoTask and can be inserted back into it
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class NanoTaskPrintedComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The task that this item holds
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public NanoTaskItem? Task;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public enum NanoTaskPriority : byte
|
|||
/// The data relating to a single NanoTask item, but not its identifier
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable, DataRecord]
|
||||
public sealed partial class NanoTaskItem
|
||||
public sealed partial class NanoTaskItem : IRobustCloneable<NanoTaskItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum length of the Description and TaskIsFor fields
|
||||
|
|
@ -51,10 +51,16 @@ public sealed partial class NanoTaskItem
|
|||
IsTaskDone = isTaskDone;
|
||||
Priority = priority;
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
{
|
||||
return Description.Length <= MaximumStringLength && TaskIsFor.Length <= MaximumStringLength;
|
||||
}
|
||||
|
||||
public NanoTaskItem Clone()
|
||||
{
|
||||
return new(Description, TaskIsFor, IsTaskDone, Priority);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class NetProbeCartridgeComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of probed network devices
|
||||
/// </summary>
|
||||
[DataField("probedDevices")]
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<ProbedNetworkDevice> ProbedDevices = new();
|
||||
|
||||
/// <summary>
|
||||
/// Limits the amount of devices that can be saved
|
||||
/// </summary>
|
||||
[DataField("maxSavedDevices")]
|
||||
public int MaxSavedDevices { get; set; } = 9;
|
||||
[DataField, AutoNetworkedField]
|
||||
public int MaxSavedDevices = 9;
|
||||
|
||||
[DataField("soundScan")]
|
||||
[DataField]
|
||||
public SoundSpecifier SoundScan = new SoundPathSpecifier("/Audio/Machines/scan_finish.ogg");
|
||||
}
|
||||
|
||||
|
|
@ -1,61 +1,54 @@
|
|||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Content.Shared.DeviceNetwork;
|
||||
using Content.Shared.DeviceNetwork.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
public sealed partial class NetProbeCartridgeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private CartridgeLoaderSystem _cartridgeLoaderSystem = default!;
|
||||
[Dependency] private IRobustRandom _random = default!;
|
||||
[Dependency] private SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private SharedAudioSystem _audioSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NetProbeCartridgeComponent, CartridgeUiReadyEvent>(OnUiReady);
|
||||
SubscribeLocalEvent<NetProbeCartridgeComponent, CartridgeAfterInteractEvent>(AfterInteract);
|
||||
SubscribeLocalEvent<NetProbeCartridgeComponent, CartridgeRelayedEvent<AfterInteractEvent>>(AfterInteract);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="CartridgeAfterInteractEvent" /> gets relayed to this system if the cartridge loader is running
|
||||
/// the NetProbe program and someone clicks on something with it. <br/>
|
||||
/// <br/>
|
||||
/// Saves name, address... etc. of the device that was clicked into a list on the component when the device isn't already present in that list
|
||||
/// </summary>
|
||||
private void AfterInteract(EntityUid uid, NetProbeCartridgeComponent component, CartridgeAfterInteractEvent args)
|
||||
private void AfterInteract(EntityUid uid, NetProbeCartridgeComponent component, CartridgeRelayedEvent<AfterInteractEvent> args)
|
||||
{
|
||||
if (args.InteractEvent.Handled || !args.InteractEvent.CanReach || !args.InteractEvent.Target.HasValue)
|
||||
if (args.Args.Handled || !args.Args.CanReach || !args.Args.Target.HasValue)
|
||||
return;
|
||||
|
||||
var target = args.InteractEvent.Target.Value;
|
||||
DeviceNetworkComponent? networkComponent = default;
|
||||
var target = args.Args.Target.Value;
|
||||
|
||||
if (!Resolve(target, ref networkComponent, false))
|
||||
if (!TryComp<DeviceNetworkComponent>(target, out var networkComponent))
|
||||
return;
|
||||
|
||||
//Ceck if device is already present in list
|
||||
// Check if device is already present in list
|
||||
foreach (var probedDevice in component.ProbedDevices)
|
||||
{
|
||||
if (probedDevice.Address == networkComponent.Address)
|
||||
return;
|
||||
}
|
||||
|
||||
//Play scanning sound with slightly randomized pitch
|
||||
//Why is there no NextFloat(float min, float max)???
|
||||
var audioParams = AudioParams.Default.WithVolume(-2f).WithPitchScale((float)_random.Next(12, 21) / 10);
|
||||
_audioSystem.PlayEntity(component.SoundScan, args.InteractEvent.User, target, audioParams);
|
||||
_popupSystem.PopupCursor(Loc.GetString("net-probe-scan", ("device", target)), args.InteractEvent.User);
|
||||
// Play scanning sound with slightly randomized pitch
|
||||
// Why is there no NextFloat(float min, float max)???
|
||||
var audioParams = AudioParams.Default.WithVolume(-2f).WithVariation(0.2f);
|
||||
_audioSystem.PlayPredicted(component.SoundScan, target, args.Args.User, audioParams);
|
||||
_popupSystem.PopupPredictedCursor(Loc.GetString("net-probe-scan", ("device", target)), args.Args.User);
|
||||
|
||||
|
||||
//Limit the amount of saved probe results to 9
|
||||
//This is hardcoded because the UI doesn't support a dynamic number of results
|
||||
// Limit the amount of saved probe results to 9
|
||||
// This is hardcoded because the UI doesn't support a dynamic number of results
|
||||
if (component.ProbedDevices.Count >= component.MaxSavedDevices)
|
||||
component.ProbedDevices.RemoveAt(0);
|
||||
|
||||
|
|
@ -67,6 +60,7 @@ public sealed partial class NetProbeCartridgeSystem : EntitySystem
|
|||
);
|
||||
|
||||
component.ProbedDevices.Add(device);
|
||||
Dirty(uid, component);
|
||||
UpdateUiState(uid, args.Loader, component);
|
||||
}
|
||||
|
||||
|
|
@ -84,6 +78,6 @@ public sealed partial class NetProbeCartridgeSystem : EntitySystem
|
|||
return;
|
||||
|
||||
var state = new NetProbeUiState(component.ProbedDevices);
|
||||
_cartridgeLoaderSystem?.UpdateCartridgeUiState(loaderUid, state);
|
||||
_cartridgeLoaderSystem.UpdateCartridgeUiState(loaderUid, state);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
[RegisterComponent]
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class NewsReaderCartridgeComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
[RegisterComponent]
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class NotekeeperCartridgeComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of notes that got written down
|
||||
/// </summary>
|
||||
[DataField("notes")]
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<string> Notes = new();
|
||||
}
|
||||
|
|
@ -1,18 +1,17 @@
|
|||
using Content.Server.Administration.Logs;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
public sealed partial class NotekeeperCartridgeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private CartridgeLoaderSystem _cartridgeLoaderSystem = default!;
|
||||
[Dependency] private IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private ISharedAdminLogManager _adminLogger = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NotekeeperCartridgeComponent, CartridgeMessageEvent>(OnUiMessage);
|
||||
SubscribeLocalEvent<NotekeeperCartridgeComponent, CartridgeUiReadyEvent>(OnUiReady);
|
||||
}
|
||||
|
|
@ -49,6 +48,7 @@ public sealed partial class NotekeeperCartridgeSystem : EntitySystem
|
|||
$"{ToPrettyString(args.Actor)} removed a note from PDA: '{message.Note}' was contained on: {ToPrettyString(uid)}");
|
||||
}
|
||||
|
||||
Dirty(uid, component);
|
||||
UpdateUiState(uid, GetEntity(args.LoaderUid), component);
|
||||
}
|
||||
|
||||
|
|
@ -58,6 +58,6 @@ public sealed partial class NotekeeperCartridgeSystem : EntitySystem
|
|||
return;
|
||||
|
||||
var state = new NotekeeperUiState(component.Notes);
|
||||
_cartridgeLoaderSystem?.UpdateCartridgeUiState(loaderUid, state);
|
||||
_cartridgeLoaderSystem.UpdateCartridgeUiState(loaderUid, state);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
public abstract class SharedNanoTaskCartridgeSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NanoTaskCartridgeComponent, CartridgeAddedEvent>(OnCartridgeAdded);
|
||||
}
|
||||
|
||||
private void OnCartridgeAdded(Entity<NanoTaskCartridgeComponent> ent, ref CartridgeAddedEvent args)
|
||||
{
|
||||
EnsureComp<NanoTaskInteractionComponent>(args.Loader);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class WantedListCartridgeComponent : Component;
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
using Content.Shared.Containers.ItemSlots;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Shared.CartridgeLoader;
|
||||
|
||||
public abstract partial class SharedCartridgeLoaderSystem : EntitySystem
|
||||
{
|
||||
public const string InstalledContainerId = "program-container";
|
||||
|
||||
[Dependency] private ItemSlotsSystem _itemSlotsSystem = default!;
|
||||
[Dependency] private SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private SharedContainerSystem _container = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, ComponentRemove>(OnComponentRemove);
|
||||
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, EntInsertedIntoContainerMessage>(OnItemInserted);
|
||||
SubscribeLocalEvent<CartridgeLoaderComponent, EntRemovedFromContainerMessage>(OnItemRemoved);
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, CartridgeLoaderComponent loader, ComponentInit args)
|
||||
{
|
||||
_itemSlotsSystem.AddItemSlot(uid, CartridgeLoaderComponent.CartridgeSlotId, loader.CartridgeSlot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks installed program entities for deletion when the component gets removed
|
||||
/// </summary>
|
||||
private void OnComponentRemove(EntityUid uid, CartridgeLoaderComponent loader, ComponentRemove args)
|
||||
{
|
||||
_itemSlotsSystem.RemoveItemSlot(uid, loader.CartridgeSlot);
|
||||
if (_container.TryGetContainer(uid, InstalledContainerId, out var cont))
|
||||
_container.ShutdownContainer(cont);
|
||||
}
|
||||
|
||||
protected virtual void OnItemInserted(EntityUid uid, CartridgeLoaderComponent loader, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
UpdateAppearanceData(uid, loader);
|
||||
}
|
||||
|
||||
protected virtual void OnItemRemoved(EntityUid uid, CartridgeLoaderComponent loader, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
UpdateAppearanceData(uid, loader);
|
||||
}
|
||||
|
||||
private void UpdateAppearanceData(EntityUid uid, CartridgeLoaderComponent loader)
|
||||
{
|
||||
_appearanceSystem.SetData(uid, CartridgeLoaderVisuals.CartridgeInserted, loader.CartridgeSlot.HasItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to program / cartridge entities when they get inserted or installed
|
||||
/// </summary>
|
||||
public sealed class CartridgeAddedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Loader;
|
||||
|
||||
public CartridgeAddedEvent(EntityUid loader)
|
||||
{
|
||||
Loader = loader;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to cartridge entities when they get ejected
|
||||
/// </summary>
|
||||
public sealed class CartridgeRemovedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Loader;
|
||||
|
||||
public CartridgeRemovedEvent(EntityUid loader)
|
||||
{
|
||||
Loader = loader;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to program / cartridge entities when they get activated
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Don't update the programs ui state in this events listener
|
||||
/// </remarks>
|
||||
public sealed class CartridgeActivatedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Loader;
|
||||
|
||||
public CartridgeActivatedEvent(EntityUid loader)
|
||||
{
|
||||
Loader = loader;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to program / cartridge entities when they get deactivated
|
||||
/// </summary>
|
||||
public sealed class CartridgeDeactivatedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Loader;
|
||||
|
||||
public CartridgeDeactivatedEvent(EntityUid loader)
|
||||
{
|
||||
Loader = loader;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent to program / cartridge entities when the ui is ready to be updated by the cartridge.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used for the initial ui state update because updating the ui in the activate event doesn't work
|
||||
/// </remarks>
|
||||
public sealed class CartridgeUiReadyEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Loader;
|
||||
|
||||
public CartridgeUiReadyEvent(EntityUid loader)
|
||||
{
|
||||
Loader = loader;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sent by the cartridge loader system to the cartridge loader entity so another system
|
||||
/// can handle displaying the notification
|
||||
/// </summary>
|
||||
/// <param name="Message">The message to be displayed</param>
|
||||
[ByRefEvent]
|
||||
public record struct CartridgeLoaderNotificationSentEvent(string Header, string Message);
|
||||
|
|
@ -54,7 +54,8 @@
|
|||
PDA-pen: !type:ContainerSlot {}
|
||||
PDA-pai: !type:ContainerSlot {}
|
||||
Cartridge-Slot: !type:ContainerSlot {}
|
||||
program-container: !type:Container
|
||||
preinstalled-program-container: !type:Container
|
||||
removable-program-container: !type:Container
|
||||
- type: ItemSlots
|
||||
- type: Clothing
|
||||
quickEquip: false
|
||||
|
|
@ -79,11 +80,6 @@
|
|||
range: 500
|
||||
- type: CartridgeLoader
|
||||
uiKey: enum.PdaUiKey.Key
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
cartridgeSlot:
|
||||
priority: -1
|
||||
name: device-pda-slot-component-slot-name-cartridge
|
||||
|
|
@ -92,6 +88,14 @@
|
|||
whitelist:
|
||||
components:
|
||||
- Cartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- type: ActivatableUI
|
||||
key: enum.PdaUiKey.Key
|
||||
singleUser: true
|
||||
|
|
@ -149,27 +153,30 @@
|
|||
id: BaseSecurityPDA
|
||||
abstract: true
|
||||
components:
|
||||
- type: CartridgeLoader
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
- WantedListCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- id: WantedListCartridge
|
||||
|
||||
- type: entity
|
||||
parent: BasePDA
|
||||
id: BaseMedicalPDA
|
||||
abstract: true
|
||||
components:
|
||||
- type: CartridgeLoader
|
||||
uiKey: enum.PdaUiKey.Key
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
- MedTekCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- id: MedTekCartridge
|
||||
|
||||
- type: entity
|
||||
parent: BasePDA
|
||||
|
|
@ -414,9 +421,6 @@
|
|||
whitelist:
|
||||
components:
|
||||
- Cartridge
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NewsReaderCartridge
|
||||
- type: Appearance
|
||||
appearanceDataInit:
|
||||
enum.PdaVisuals.PdaType:
|
||||
|
|
@ -428,6 +432,12 @@
|
|||
- type: Icon
|
||||
state: pda-mime
|
||||
- type: Muted # for random sentience event
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NewsReaderCartridge
|
||||
|
||||
- type: entity
|
||||
name: chaplain PDA
|
||||
|
|
@ -476,13 +486,15 @@
|
|||
accentVColor: "#a23e3e"
|
||||
- type: Icon
|
||||
state: pda-qm
|
||||
- type: CartridgeLoader
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
- AstroNavCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- id: AstroNavCartridge
|
||||
|
||||
- type: entity
|
||||
parent: BasePDA
|
||||
|
|
@ -520,14 +532,15 @@
|
|||
accentVColor: "#8900c9"
|
||||
- type: Icon
|
||||
state: pda-miner
|
||||
- type: CartridgeLoader
|
||||
uiKey: enum.PdaUiKey.Key
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
- AstroNavCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- id: AstroNavCartridge
|
||||
|
||||
- type: entity
|
||||
parent: BasePDA
|
||||
|
|
@ -870,14 +883,16 @@
|
|||
accentHColor: "#447987"
|
||||
- type: Icon
|
||||
state: pda-hos
|
||||
- type: CartridgeLoader
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
- WantedListCartridge
|
||||
- LogProbeCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- id: WantedListCartridge
|
||||
- id: LogProbeCartridge
|
||||
|
||||
- type: entity
|
||||
parent: BaseSecurityPDA
|
||||
|
|
@ -939,17 +954,18 @@
|
|||
borderColor: "#00842e"
|
||||
- type: Icon
|
||||
state: pda-centcom
|
||||
- type: CartridgeLoader
|
||||
uiKey: enum.PdaUiKey.Key
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
- MedTekCartridge
|
||||
- WantedListCartridge
|
||||
- LogProbeCartridge
|
||||
- AstroNavCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- id: MedTekCartridge
|
||||
- id: WantedListCartridge
|
||||
- id: LogProbeCartridge
|
||||
- id: AstroNavCartridge
|
||||
|
||||
- type: entity
|
||||
parent: CentcomPDA
|
||||
|
|
@ -965,17 +981,19 @@
|
|||
scanDelay: 0
|
||||
silent: true
|
||||
- type: CartridgeLoader
|
||||
uiKey: enum.PdaUiKey.Key
|
||||
notificationsEnabled: false
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
- LogProbeCartridge
|
||||
- WantedListCartridge
|
||||
- MedTekCartridge
|
||||
- AstroNavCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- id: LogProbeCartridge
|
||||
- id: WantedListCartridge
|
||||
- id: MedTekCartridge
|
||||
- id: AstroNavCartridge
|
||||
- type: Tag # Ignore Chameleon tags
|
||||
tags:
|
||||
- DoorBumpOpener
|
||||
|
|
@ -1089,11 +1107,12 @@
|
|||
borderColor: "#891417"
|
||||
- type: Icon
|
||||
state: pda-syndi
|
||||
- type: CartridgeLoader
|
||||
uiKey: enum.PdaUiKey.Key
|
||||
preinstalled:
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
|
||||
- type: entity
|
||||
parent: [ SyndiPDA, BaseSyndicateContraband ]
|
||||
|
|
@ -1126,12 +1145,13 @@
|
|||
pda-syndi-corpsman
|
||||
- type: Icon
|
||||
state: pda-syndi-corpsman
|
||||
- type: CartridgeLoader
|
||||
uiKey: enum.PdaUiKey.Key
|
||||
preinstalled:
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- MedTekCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: MedTekCartridge
|
||||
|
||||
- type: entity
|
||||
parent: [ SyndiOperativePDA, BaseSyndicateContraband ]
|
||||
|
|
@ -1169,17 +1189,18 @@
|
|||
accentVColor: "#447987"
|
||||
- type: Icon
|
||||
state: pda-ert
|
||||
- type: CartridgeLoader
|
||||
uiKey: enum.PdaUiKey.Key
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
- MedTekCartridge
|
||||
- WantedListCartridge
|
||||
- LogProbeCartridge
|
||||
- AstroNavCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- id: MedTekCartridge
|
||||
- id: WantedListCartridge
|
||||
- id: LogProbeCartridge
|
||||
- id: AstroNavCartridge
|
||||
|
||||
- type: entity
|
||||
parent: ERTLeaderPDA
|
||||
|
|
@ -1341,14 +1362,16 @@
|
|||
borderColor: "#774705"
|
||||
- type: Icon
|
||||
state: pda-detective
|
||||
- type: CartridgeLoader
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
- WantedListCartridge
|
||||
- LogProbeCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- id: WantedListCartridge
|
||||
- id: LogProbeCartridge
|
||||
|
||||
- type: entity
|
||||
parent: BaseMedicalPDA
|
||||
|
|
@ -1369,14 +1392,16 @@
|
|||
accentVColor: "#d7d7d0"
|
||||
- type: Icon
|
||||
state: pda-brigmedic
|
||||
- type: CartridgeLoader
|
||||
preinstalled:
|
||||
- CrewManifestCartridge
|
||||
- NotekeeperCartridge
|
||||
- NanoTaskCartridge
|
||||
- NewsReaderCartridge
|
||||
- WantedListCartridge
|
||||
- MedTekCartridge
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
preinstalled-program-container: !type:AllSelector
|
||||
children:
|
||||
- id: CrewManifestCartridge
|
||||
- id: NotekeeperCartridge
|
||||
- id: NanoTaskCartridge
|
||||
- id: NewsReaderCartridge
|
||||
- id: WantedListCartridge
|
||||
- id: MedTekCartridge
|
||||
|
||||
- type: entity
|
||||
parent: ClownPDA
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue