Give silicons PDAs (#5738)

* Give silicons PDAs

* unsaved

* borgos can have a little notification as a treat

* .Value
This commit is contained in:
pathetic meowmeow 2026-05-09 18:19:47 -04:00 committed by GitHub
parent 4093522aa7
commit ab62d18431
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 351 additions and 40 deletions

View File

@ -1,4 +1,5 @@
using Content.Shared.Movement.Components;
using Content.Client.PDA; // DeltaV
using Content.Shared.Movement.Components;
using Content.Shared.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components;
using Robust.Client.GameObjects;
@ -66,6 +67,15 @@ public sealed partial class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeS
}
}
// DeltaV - borg pdas
if (TryComp<PdaBorderColorComponent>(entity, out var pdaBorders))
{
pdaBorders.BorderColor = prototype.PdaBorderColor ?? pdaBorders.BorderColor;
pdaBorders.AccentHColor = prototype.PdaAccentHorizontalColor ?? pdaBorders.AccentHColor;
pdaBorders.AccentVColor = prototype.PdaAccentVerticalColor ?? pdaBorders.AccentVColor;
}
// DeltaV - borg pdas
base.UpdateEntityAppearance(entity, prototype);
}
}

View File

@ -18,6 +18,8 @@ using Content.Shared.Light;
using Content.Shared.Light.EntitySystems;
using Content.Shared.PDA;
using Content.Shared.PDA.Ringer;
using Content.Shared.Silicons.Borgs.Components; // DeltaV - silicon PDAs
using Content.Shared.Silicons.StationAi; // DeltaV - silicon PDAs
using Robust.Server.Containers;
using Robust.Server.GameObjects;
using Robust.Shared.Configuration; // DeltaV - PDA date
@ -168,9 +170,12 @@ namespace Content.Server.PDA
{
_ringer.RingerPlayRingtone(ent.Owner);
if (!_containerSystem.TryGetContainingContainer((ent, null, null), out var container)
|| !TryComp<ActorComponent>(container.Owner, out var actor))
// Begin DeltaV - PDAs can be self-viewed
if (!(TryComp<ActorComponent>(ent, out var actor) ||
(_containerSystem.TryGetContainingContainer((ent, null, null), out var container)
&& TryComp<ActorComponent>(container.Owner, out actor))))
return;
// End DeltaV - PDAs can be self-viewed
var message = FormattedMessage.EscapeText(args.Message);
var wrappedMessage = Loc.GetString("pda-notification-message",
@ -213,6 +218,26 @@ namespace Content.Server.PDA
var programs = _cartridgeLoader.GetAvailablePrograms(uid, loader);
var id = CompOrNull<IdCardComponent>(pda.ContainedId);
// Begin DeltaV - PDAs can be silicons
var owner = id?.FullName;
var job = id?.LocalizedJobTitle;
if (HasComp<BorgChassisComponent>(uid))
{
if (TryComp<BorgSwitchableTypeComponent>(uid, out var switchable) && switchable.SelectedBorgType is { } borgType)
job = Loc.GetString($"borg-type-{borgType}-transponder");
else
job = Loc.GetString("borg-type-any-transponder");
owner = MetaData(uid).EntityName;
}
if (HasComp<StationAiHeldComponent>(uid))
{
job = Loc.GetString($"station-ai-transponder");
owner = MetaData(uid).EntityName;
}
// End DeltaV - PDAs can be silicons
var state = new PdaUpdateState(
programs,
GetNetEntity(loader.ActiveProgram),
@ -222,8 +247,8 @@ namespace Content.Server.PDA
new PdaIdInfoText
{
ActualOwnerName = pda.OwnerName,
IdOwner = id?.FullName,
JobTitle = id?.LocalizedJobTitle,
IdOwner = owner, // DeltaV - silicon PDAs
JobTitle = job, // DeltaV - silicon PDAs
CurrentDate = pda.CurrentDate, // DeltaV - PDA date
StationAlertLevel = pda.StationAlertLevel,
StationAlertColor = pda.StationAlertColor

View File

@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Administration.Logs;
using Content.Server.CartridgeLoader;
@ -12,6 +13,8 @@ using Content.Shared._DV.CartridgeLoader.Cartridges;
using Content.Shared._DV.NanoChat;
using Content.Shared.PDA;
using Content.Shared.Radio.Components;
using Content.Shared.Silicons.Borgs.Components;
using Content.Shared.Silicons.StationAi;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
@ -63,13 +66,10 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
private void OnActiveProgramChanged(Entity<CartridgeLoaderComponent> ent, ref ActiveProgramChangedEvent args)
{
if (!_pdaQuery.TryGetComponent(ent, out var pda) || pda.ContainedId is not { } cardUid)
if (!GetCardEntity(ent, out var nanoChatCard))
return;
if (!_cardQuery.TryGetComponent(cardUid, out var nanoChatCard))
return;
_nanoChat.SetClosed((cardUid, nanoChatCard), !HasComp<NanoChatCartridgeComponent>(args.NewActiveProgram));
_nanoChat.SetClosed(nanoChatCard.Value.AsNullable(), !HasComp<NanoChatCartridgeComponent>(args.NewActiveProgram));
}
private void OnUiOpened(Entity<CartridgeLoaderComponent> ent, ref BoundUIOpenedEvent args)
@ -77,14 +77,11 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
if (!PdaUiKey.Key.Equals(args.UiKey))
return;
if (!_pdaQuery.TryGetComponent(ent, out var pda) || pda.ContainedId is not { } cardUid)
if (!GetCardEntity(ent, out var nanoChatCard))
return;
if (!_cardQuery.TryGetComponent(cardUid, out var nanoChatCard))
return;
if (nanoChatCard.IsClosed)
_nanoChat.SetClosed((cardUid, nanoChatCard), !HasComp<NanoChatCartridgeComponent>(ent.Comp.ActiveProgram));
if (nanoChatCard.Value.Comp.IsClosed)
_nanoChat.SetClosed(nanoChatCard.Value.AsNullable(), !HasComp<NanoChatCartridgeComponent>(ent.Comp.ActiveProgram));
}
@ -93,15 +90,12 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
if (!PdaUiKey.Key.Equals(args.UiKey))
return;
if (!_pdaQuery.TryGetComponent(ent, out var pda) || pda.ContainedId is not { } cardUid)
return;
if (!_cardQuery.TryGetComponent(cardUid, out var nanoChatCard))
if (!GetCardEntity(ent, out var nanoChatCard))
return;
// Since the UI got closed we always set it to be closed
if (!nanoChatCard.IsClosed)
_nanoChat.SetClosed((cardUid, nanoChatCard), true);
if (!nanoChatCard.Value.Comp.IsClosed)
_nanoChat.SetClosed(nanoChatCard.Value.AsNullable(), true);
}
public override void Update(float frameTime)
@ -120,7 +114,8 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
continue; // TODO Perf: this could be faster if we get rid of the trycomp in the update loop altogether
// There was a reason I did this in the Update loop but I can't remember.
var newCard = pda.ContainedId;
GetCardEntity(cartridge.LoaderUid.Value, out var newCardEnt);
var newCard = newCardEnt?.Owner;
var currentCard = nanoChat.Card;
// If the cards match, nothing to do
@ -149,31 +144,31 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
switch (msg.Type)
{
case NanoChatUiMessageType.NewChat:
HandleNewChat(card, msg);
HandleNewChat(card.Value, msg);
break;
case NanoChatUiMessageType.SelectChat:
HandleSelectChat(card, msg);
HandleSelectChat(card.Value, msg);
break;
case NanoChatUiMessageType.EditChat:
HandleEditChat(card, msg);
HandleEditChat(card.Value, msg);
break;
case NanoChatUiMessageType.CloseChat:
HandleCloseChat(card);
HandleCloseChat(card.Value);
break;
case NanoChatUiMessageType.ToggleMute:
HandleToggleMute(card);
HandleToggleMute(card.Value);
break;
case NanoChatUiMessageType.ToggleMuteChat:
HandleToggleMuteChat(card, msg);
HandleToggleMuteChat(card.Value, msg);
break;
case NanoChatUiMessageType.DeleteChat:
HandleDeleteChat(card, msg);
HandleDeleteChat(card.Value, msg);
break;
case NanoChatUiMessageType.SendMessage:
HandleSendMessage(ent, card, msg);
HandleSendMessage(ent, card.Value, msg);
break;
case NanoChatUiMessageType.ToggleListNumber:
HandleToggleListNumber(card);
HandleToggleListNumber(card.Value);
break;
}
@ -188,9 +183,15 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
/// <returns>True if a valid NanoChat card was found</returns>
private bool GetCardEntity(
EntityUid loaderUid,
out Entity<NanoChatCardComponent> card)
[NotNullWhen(true)] out Entity<NanoChatCardComponent>? card)
{
card = default;
card = null;
if (TryComp<NanoChatCardComponent>(loaderUid, out var selfCard))
{
card = (loaderUid, selfCard);
return true;
}
// Get the PDA and check if it has an ID card
if (!_pdaQuery.TryComp(loaderUid, out var pda) ||
@ -202,6 +203,30 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
return true;
}
/// <summary>
/// Gets the cartridge loader associated with a card.
/// </summary>
private bool GetCartridgeLoader(
Entity<NanoChatCardComponent> card,
[NotNullWhen(true)] out Entity<CartridgeLoaderComponent>? loader)
{
loader = null;
if (TryComp<CartridgeLoaderComponent>(card, out var selfLoader))
{
loader = (card, selfLoader);
return true;
}
if (card.Comp.PdaUid is { } pdaUid && TryComp<CartridgeLoaderComponent>(pdaUid, out var pdaLoader))
{
loader = (pdaUid, pdaLoader);
return true;
}
return false;
}
/// <summary>
/// Handles creation of a new chat conversation.
/// </summary>
@ -562,12 +587,11 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
HashSet<uint> mutedChats = recipient.Comp.MutedChats;
if (recipient.Comp.NotificationsMuted ||
mutedChats.Contains(message.SenderId) ||
recipient.Comp.PdaUid is not { } pdaUid ||
!TryComp<CartridgeLoaderComponent>(pdaUid, out var loader) ||
!GetCartridgeLoader(recipient, out var loader) ||
// Don't notify if the recipient has the NanoChat program open with this chat selected.
(hasSelectedCurrentChat &&
_ui.IsUiOpen(pdaUid, PdaUiKey.Key) &&
HasComp<NanoChatCartridgeComponent>(loader.ActiveProgram)))
_ui.IsUiOpen(loader.Value.Owner, PdaUiKey.Key) &&
HasComp<NanoChatCartridgeComponent>(loader.Value.Comp.ActiveProgram)))
return;
var title = "";
@ -580,10 +604,10 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
else
title = Loc.GetString("nano-chat-new-message-title", ("sender", senderName));
_cartridge.SendNotification(pdaUid,
_cartridge.SendNotification(loader.Value,
title,
Loc.GetString("nano-chat-new-message-body", ("message", SharedNanoChatSystem.Truncate(message.Content, NotificationMaxLength, " [...]"))),
loader);
loader.Value);
}
/// <summary>
@ -628,6 +652,18 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
if (card.Number != number)
continue;
if (HasComp<BorgChassisComponent>(uid))
{
if (!TryComp<BorgSwitchableTypeComponent>(uid, out var switchable) || switchable.SelectedBorgType is not { } borgType)
return new NanoChatRecipient(number, MetaData(uid).EntityName, null);
return new NanoChatRecipient(number, MetaData(uid).EntityName, Loc.GetString($"borg-type-{borgType}-transponder"));
}
if (HasComp<StationAiHeldComponent>(uid))
{
return new NanoChatRecipient(number, MetaData(uid).EntityName, Loc.GetString($"station-ai-transponder"));
}
// Try to get job title from ID card if possible
string? jobTitle = null;
var name = "Unknown";
@ -666,6 +702,25 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
contacts.Add(new NanoChatRecipient(nanoChatNumber, fullName));
}
}
var borgQuery = AllEntityQuery<NanoChatCardComponent, BorgChassisComponent>();
while (borgQuery.MoveNext(out var borgId, out var borgChatCard, out var _))
{
if (borgChatCard.ListNumber && borgChatCard.Number is uint nanoChatNumber && _station.GetOwningStation(borgId) == station)
{
contacts.Add(new NanoChatRecipient(nanoChatNumber, MetaData(borgId).EntityName));
}
}
var aiQuery = AllEntityQuery<NanoChatCardComponent, StationAiHeldComponent>();
while (aiQuery.MoveNext(out var aiId, out var aiChatCard, out var _))
{
if (aiChatCard.ListNumber && aiChatCard.Number is uint nanoChatNumber && _station.GetOwningStation(aiId) == station)
{
contacts.Add(new NanoChatRecipient(nanoChatNumber, MetaData(aiId).EntityName));
}
}
contacts.Sort((contactA, contactB) => string.CompareOrdinal(contactA.Name, contactB.Name));
}
else

View File

@ -164,6 +164,17 @@ public sealed partial class BorgTypePrototype : IPrototype
[DataField]
public string PetFailureString { get; set; } = "petting-failure-generic-cyborg";
// Begin DeltaV
[DataField]
public string? PdaBorderColor;
[DataField]
public string? PdaAccentHorizontalColor;
[DataField]
public string? PdaAccentVerticalColor;
// End DeltaV
//
// Sounds
//

View File

@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Silicon;
/// <summary>
/// Component that indicates a built-in PDA on a cyborg that can be accessed by the AI
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class BorgPdaComponent : Component;

View File

@ -0,0 +1,36 @@
using Content.Shared.PDA;
using Content.Shared.Silicons.StationAi;
using Content.Shared.Verbs;
namespace Content.Shared._DV.Silicon;
public sealed class BorgPdaSystem : EntitySystem
{
[Dependency] private readonly SharedUserInterfaceSystem _userInterface = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BorgPdaComponent, GetVerbsEvent<AlternativeVerb>>(OnGetAlternativeVerbs);
}
private void OnGetAlternativeVerbs(Entity<BorgPdaComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanComplexInteract || !HasComp<StationAiHeldComponent>(args.User) || !args.CanInteract)
return;
var user = args.User;
var target = args.Target;
var verb = new AlternativeVerb
{
Text = Loc.GetString("toggle-borg-pda"),
Act = () =>
{
_userInterface.TryToggleUi(target, PdaUiKey.Key, user);
}
};
args.Verbs.Add(verb);
}
}

View File

@ -0,0 +1,30 @@
using Content.Shared.Actions;
using Content.Shared.PDA;
using Robust.Shared.GameObjects;
using Robust.Shared.Player;
namespace Content.Shared._DV.Silicon;
public sealed class SiliconPdaScreenSystem : EntitySystem
{
[Dependency] private readonly SharedUserInterfaceSystem _userInterface = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PdaComponent, TogglePdaScreenEvent>(OnTogglePdaScreen);
}
private void OnTogglePdaScreen(Entity<PdaComponent> ent, ref TogglePdaScreenEvent args)
{
if (args.Handled || !TryComp<ActorComponent>(ent, out var actor))
return;
args.Handled = true;
_userInterface.TryToggleUi(ent.Owner, PdaUiKey.Key, actor.PlayerSession);
}
}
public sealed partial class TogglePdaScreenEvent : InstantActionEvent;

View File

@ -3,3 +3,7 @@ borg-type-security-desc = Assist security in the fight for justice by detaining
borg-type-security-transponder = security cyborg
brain-cannot-be-borged-message = [color=red]This brain is damaged beyond use.[/color]
borg-type-any-transponder = cyborg
station-ai-transponder = station ai
toggle-borg-pda = View PDA

View File

@ -75,6 +75,12 @@
# Only used for NT borgs that can switch type, defined here to avoid copy-pasting the rest of this component.
enum.BorgSwitchableTypeUiKey.SelectBorgType:
type: BorgSelectTypeUserInterface
# Begin DeltaV - borg PDAs
enum.PdaUiKey.Key:
type: PdaBoundUserInterface
enum.RingerUiKey.Key:
type: RingerBoundUserInterface
# End DeltaV - borg PDAs
- type: Targeting # Shitmed Change
- type: ActivatableUI
key: enum.BorgUiKey.Key
@ -130,6 +136,8 @@
borg_id_chip: !type:ContainerSlot # DeltaV - borg id chips
borg_module: !type:Container { }
part-container: !type:Container
program-container: !type:Container # DeltaV - Borg PDAs
Cartridge-Slot: !type:ContainerSlot # DeltaV - Borg PDAs
- type: PowerCellSlot
cellSlotId: cell_slot
fitsInCharger: true

View File

@ -31,6 +31,41 @@
inherentRadioChannels:
- Common
- Binary
# Begin Impstation - Borg PDAs
- type: Pda
idSlot:
locked: true
penSlot:
locked: true
paiSlot:
locked: true
- type: PdaBorderColor
borderColor: "#717059"
- type: Ringer
- type: NanoChatCard
- type: CartridgeLoader
uiKey: enum.PdaUiKey.Key
preinstalled:
- CrewManifestCartridge
- NotekeeperCartridge
- NanoTaskCartridge
- NewsReaderCartridge
- NanoChatCartridge
cartridgeSlot:
locked: true
priority: -1
name: device-pda-slot-component-slot-name-cartridge
ejectSound: /Audio/Machines/id_swipe.ogg
insertSound: /Audio/Machines/id_insert.ogg
whitelist:
components:
- Cartridge
- type: ActionGrant
actions:
- ActionOpenCartridges
- type: StationAiWhitelist
- type: BorgPda
# End Impstation - Borg PDAs
- type: entity
id: BorgChassisGeneric

View File

@ -1,6 +1,7 @@
# Be careful with these as they get removed on shutdown too!
- type: entity
id: AiHeld
save: false # DeltaV - AI PDAs
description: Components added / removed from an entity that gets inserted into an AI core.
categories: [ HideSpawnMenu ]
components:
@ -46,6 +47,7 @@
- ActionJumpToCore
- ActionSurvCameraLights
- ActionAIViewLaws
- ActionOpenCartridges # DeltaV - AI PDA
- type: UserInterface
interfaces:
enum.RadarConsoleUiKey.Key:
@ -60,6 +62,12 @@
type: CommunicationsConsoleBoundUserInterface
enum.StoreUiKey.Key: # DeltaV - AI shop system
type: StoreBoundUserInterface
# Begin DeltaV - AI PDAs
enum.PdaUiKey.Key:
type: PdaBoundUserInterface
enum.RingerUiKey.Key:
type: RingerBoundUserInterface
# End DeltaV - AI PDAs
- type: IntrinsicUI
uis:
enum.RadarConsoleUiKey.Key:
@ -418,6 +426,7 @@
# The actual brain inside the core
- type: entity
id: StationAiBrain
save: false # DeltaV - AI PDAs
parent: PositronicBrain
categories: [ HideSpawnMenu, DoNotMap ]
components:
@ -495,6 +504,39 @@
mindRoles:
- MindRoleGhostRoleSilicon
job: StationAi
# Begin DeltaV - AI PDAs
- type: Pda
idSlot:
locked: true
penSlot:
locked: true
paiSlot:
locked: true
- type: Ringer
- type: NanoChatCard
- type: CartridgeLoader
uiKey: enum.PdaUiKey.Key
preinstalled:
- NewsReaderCartridge
- NanoChatCartridge
cartridgeSlot:
locked: true
priority: -1
name: device-pda-slot-component-slot-name-cartridge
ejectSound: /Audio/Machines/id_swipe.ogg
insertSound: /Audio/Machines/id_insert.ogg
whitelist:
components:
- Cartridge
- type: ContainerContainer
containers:
program-container: !type:Container
Cartridge-Slot: !type:ContainerSlot
- type: ItemSlots
- type: PdaBorderColor
borderColor: "#45453f"
accentVColor: "#447987"
# End DeltaV - AI PDAs
# Hologram projection that the AI's eye tracks.
- type: entity

View File

@ -24,3 +24,18 @@
- type: InstantAction
event: !type:FabricateCandyActionEvent
item: FoodGumball
- type: entity
parent: BaseMentalAction
id: ActionOpenCartridges
name: Open Programs
description: View software installed on you
components:
- type: Action
itemIconStyle: NoItem
icon:
sprite: _DV/Interface/Actions/actions_ai.rsi
state: pda
useDelay: 0.5
- type: InstantAction
event: !type:TogglePdaScreenEvent

View File

@ -71,3 +71,7 @@
# Pet
petSuccessString: petting-success-security-cyborg
petFailureString: petting-failure-security-cyborg
# Begin DeltaV - Borg PDAs
pdaBorderColor: "#A32D26"
# End DeltaV - Borg PDAs

View File

@ -76,6 +76,11 @@
petSuccessString: petting-success-engineer-cyborg
petFailureString: petting-failure-engineer-cyborg
# Begin DeltaV - Borg PDAs
pdaBorderColor: "#bf803a"
pdaAccentVerticalColor: "#ffd187"
# End DeltaV - Borg PDAs
# Salvage borg
- type: borgType
@ -122,6 +127,11 @@
petSuccessString: petting-success-salvage-cyborg
petFailureString: petting-failure-salvage-cyborg
# Begin DeltaV - Borg PDAs
pdaBorderColor: "#af9366"
pdaAccentVerticalColor: "#8900c9"
# End DeltaV - Borg PDAs
# Janitor borg
- type: borgType
@ -166,6 +176,10 @@
petSuccessString: petting-success-janitor-cyborg
petFailureString: petting-failure-janitor-cyborg
# Begin DeltaV - Borg PDAs
pdaBorderColor: "#5D2D56"
# End DeltaV - Borg PDAs
# Medical borg
- type: borgType
@ -231,6 +245,11 @@
footstepCollection:
collection: FootstepHoverBorg
# Begin DeltaV - Borg PDAs
pdaBorderColor: "#d7d7d0"
pdaAccentVerticalColor: "#447987"
# End DeltaV - Borg PDAs
# Service borg
- type: borgType
@ -271,3 +290,8 @@
# Pet
petSuccessString: petting-success-service-cyborg
petFailureString: petting-failure-service-cyborg
# Begin DeltaV - Borg PDAs
pdaBorderColor: "#717059"
pdaAccentVerticalColor: "#00cc35"
# End DeltaV - Borg PDAs

View File

@ -27,6 +27,9 @@
},
{
"name": "store"
},
{
"name": "pda"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B