From a9bb4921a28f8d65fe2366f043bfd6ef2e6d9531 Mon Sep 17 00:00:00 2001 From: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Date: Sat, 20 Dec 2025 08:21:54 -0600 Subject: [PATCH] Station AI ghost role (#40607) * Initial commit * API * review --------- Co-authored-by: ScarKy0 --- .../Ghost/Roles/ToggleableGhostRoleSystem.cs | 26 +++++++++++------- .../Silicons/StationAi/StationAiSystem.cs | 27 ++++++++++++++++--- .../Locale/en-US/silicons/station-ai.ftl | 4 +++ .../Entities/Mobs/Player/silicon.yml | 7 +++++ 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs b/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs index 7bb25a63fb..13f90d8bd1 100644 --- a/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs @@ -49,15 +49,23 @@ public sealed class ToggleableGhostRoleSystem : EntitySystem UpdateAppearance(uid, ToggleableGhostRoleStatus.Searching); - var ghostRole = EnsureComp(uid); - EnsureComp(uid); - - //GhostRoleComponent inherits custom settings from the ToggleableGhostRoleComponent - ghostRole.RoleName = Loc.GetString(component.RoleName); - ghostRole.RoleDescription = Loc.GetString(component.RoleDescription); - ghostRole.RoleRules = Loc.GetString(component.RoleRules); - ghostRole.JobProto = component.JobProto; - ghostRole.MindRoles = component.MindRoles; + ActivateGhostRole((uid, component)); + } + + public void ActivateGhostRole(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + var ghostRole = EnsureComp(ent); + EnsureComp(ent); + + // GhostRoleComponent inherits custom settings from the ToggleableGhostRoleComponent + ghostRole.RoleName = Loc.GetString(ent.Comp.RoleName); + ghostRole.RoleDescription = Loc.GetString(ent.Comp.RoleDescription); + ghostRole.RoleRules = Loc.GetString(ent.Comp.RoleRules); + ghostRole.JobProto = ent.Comp.JobProto; + ghostRole.MindRoles = ent.Comp.MindRoles; } private void OnExamined(EntityUid uid, ToggleableGhostRoleComponent component, ExaminedEvent args) diff --git a/Content.Server/Silicons/StationAi/StationAiSystem.cs b/Content.Server/Silicons/StationAi/StationAiSystem.cs index c4afd83b2d..36aa9acd6b 100644 --- a/Content.Server/Silicons/StationAi/StationAiSystem.cs +++ b/Content.Server/Silicons/StationAi/StationAiSystem.cs @@ -2,6 +2,8 @@ using Content.Server.Chat.Systems; using Content.Server.Construction; using Content.Server.Destructible; using Content.Server.Ghost; +using Content.Server.Ghost.Roles; +using Content.Server.Ghost.Roles.Components; using Content.Server.Mind; using Content.Server.Power.Components; using Content.Server.Roles; @@ -47,6 +49,7 @@ public sealed class StationAiSystem : SharedStationAiSystem [Dependency] private readonly RoleSystem _roles = default!; [Dependency] private readonly ItemSlotsSystem _slots = default!; [Dependency] private readonly GhostSystem _ghost = default!; + [Dependency] private readonly ToggleableGhostRoleSystem _ghostrole = default!; [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly DestructibleSystem _destructible = default!; [Dependency] private readonly SharedBatterySystem _battery = default!; @@ -97,14 +100,30 @@ public sealed class StationAiSystem : SharedStationAiSystem } var brain = container.ContainedEntities[0]; + var hasMind = _mind.TryGetMind(brain, out var mindId, out var mind); - if (_mind.TryGetMind(brain, out var mindId, out var mind)) + if (hasMind || HasComp(brain)) { - // Found an existing mind to transfer into the AI core var aiBrain = Spawn(_stationAiBrain, Transform(ent.Owner).Coordinates); - _roles.MindAddJobRole(mindId, mind, false, _stationAiJob); - _mind.TransferTo(mindId, aiBrain); + if (hasMind) + { + // Found an existing mind to transfer into the AI core + _roles.MindAddJobRole(mindId, mind, false, _stationAiJob); + _mind.TransferTo(mindId, aiBrain); + } + else + { + // If the brain had a ghost role attached, activate the station AI ghost role + _ghostrole.ActivateGhostRole(aiBrain); + + // Set the new AI brain to the 'rebooting' state + if (TryComp(aiBrain, out var customization)) + SetStationAiState((aiBrain, customization), StationAiState.Rebooting); + + } + + // Delete the new AI brain if it cannot be inserted into the core if (!TryComp(ent, out var targetHolder) || !_slots.TryInsert(ent, targetHolder.Slot, aiBrain, null)) { diff --git a/Resources/Locale/en-US/silicons/station-ai.ftl b/Resources/Locale/en-US/silicons/station-ai.ftl index e3451452e4..cbe3ef6ec0 100644 --- a/Resources/Locale/en-US/silicons/station-ai.ftl +++ b/Resources/Locale/en-US/silicons/station-ai.ftl @@ -9,6 +9,10 @@ station-ai-is-too-damaged-for-upload = Upload failed - the AI core must be repai station-ai-core-losing-power = Your AI core is now running on reserve battery power. station-ai-core-critical-power = Your AI core is critically low on power. External power must be re-established or severe data corruption may occur! +# Ghost role +station-ai-ghost-role-name = Station AI +station-ai-ghost-role-description = Serve the station crew as its ever watchful AI. + # Radial actions ai-open = Open actions ai-close = Close actions diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index 898cee8524..d9bc7c5829 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -457,6 +457,13 @@ state: ai_female - type: NameIdentifier group: StationAi + - type: ToggleableGhostRole + roleName: station-ai-ghost-role-name + roleDescription: station-ai-ghost-role-description + roleRules: ghost-role-information-silicon-rules + mindRoles: + - MindRoleGhostRoleSilicon + job: StationAi # Hologram projection that the AI's eye tracks. - type: entity -- 2.52.0