Repository: DeltaV-Station/Delta-v
Pull request #3610: Introduce an AI shop system
Author: login: sowelipililimute
Labels: name: Changes: YML
description: Changes any yml files
name: S: Needs Review
description: Awaiting review from a Maintainer
name: Changes: C#
description: Changes any cs files
name: Changes: Localization
description: Changes any ftl files
name: size/L
description: 256-1023 lines
name: Changes: Sprite
description: Changes any png or json in an rsi
Created: 2025-05-01T04:57:55Z | Merged: 2025-05-02T21:15:14Z

<!-- If you are new to the Delta-V repository, please read the [Contributing Guidelines](https://github.com/DeltaV-Station/Delta-v/blob/master/CONTRIBUTING.md) -->

## About the PR
This introduces the groundwork for allowing station AIs to select a range of extra abilities for a shift, and introduces a set of such abilities to begin with. Inspired by https://github.com/space-wizards/space-station-14/pull/36857 and https://github.com/ParadiseSS13/Paradise/pull/27988.

## Why / Balance
This allows the set of abilities an AI has to be variable per shift, and gives AIs more ways to interact with the crew besides being a glorified door opener.

## Technical details
- modify StoreSystem to be able to grant actions to the entity and not the mind
- StationAiShopSystem to grant the AI access to the shop action + implementation of the actions you can buy from it
- new action events:
  - StationAiRgbLightingActionEvent: Toggles the RGB light controller on the given entity
  - StationAiLightSynthesizerActionEvent: Replaces the light bulb in the target with the given prototype
  - StationAiPlaySoundActionEvent: Plays the given sound coming from the target entity
  - StationAiHealthChangeActionEvent: Changes the target entity's health by the given damage specifier
  - StationAiSpawnEntityActionEvent: Spawns the given entity at the target location
  - StationAiSmokeActionEvent: Triggers the given smoke effect at the target location
- usual shop & listing setup

## Media
![grafik](https://github.com/user-attachments/assets/cedbf65d-2c1d-4c95-9512-d16e5282ec1e)

## Requirements
<!-- Confirm the following by placing an X in the brackets [X]: -->
- [x] I have tested all added content and changes.
- [x] I have added media to this PR or it does not require an ingame showcase.
<!-- You should understand that not following the above may get your PR closed at maintainer’s discretion -->

## Breaking changes
<!-- List any breaking changes, including namespaces, public class/method/field changes, prototype renames; and provide instructions for fixing them. -->

**Changelog**
<!-- Add a Changelog entry to make players aware of new features or changes that could affect gameplay.
Make sure to read the guidelines and take this Changelog template out of the comment block in order for it to show up.
Changelogs must have a :cl: symbol, so the bot recognizes the changes and adds them to the game's changelog. -->
:cl:
- add: Station AI can now choose from a selection of programs roundstart to expand its abilities
<!--
:cl:
- add: Added fun!
- remove: Removed fun!
- tweak: Changed fun!
- fix: Fixed fun!
-->



Diff:
diff --git a/Content.Client/_DV/Silicons/StationAiShopSystem.cs b/Content.Client/_DV/Silicons/StationAiShopSystem.cs
new file mode 100644
index 000000000000..974004f86ff1
--- /dev/null
+++ b/Content.Client/_DV/Silicons/StationAiShopSystem.cs
@@ -0,0 +1,5 @@
+using Content.Shared._DV.Silicons;
+
+namespace Content.Client._DV.Silicons;
+
+public sealed class StationAiShopSystem : SharedStationAiShopSystem;
diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs
index 93bbd3bd9667..5515f231483e 100644
--- a/Content.Server/Store/Systems/StoreSystem.Ui.cs
+++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs
@@ -202,7 +202,7 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi
             EntityUid? actionId;
             // I guess we just allow duplicate actions?
             // Allow duplicate actions and just have a single list buy for the buy-once ones.
-            if (!_mind.TryGetMind(buyer, out var mind, out _))
+            if (!_mind.TryGetMind(buyer, out var mind, out _) || !component.GrantActionsToMind) // DeltaV - allow forcing actions to be on the entity
                 actionId = _actions.AddAction(buyer, listing.ProductAction);
             else
                 actionId = _actionContainer.AddAction(mind, listing.ProductAction);
diff --git a/Content.Server/_DV/Silicons/StationAiShopSystem.cs b/Content.Server/_DV/Silicons/StationAiShopSystem.cs
new file mode 100644
index 000000000000..edb3ffac73c4
--- /dev/null
+++ b/Content.Server/_DV/Silicons/StationAiShopSystem.cs
@@ -0,0 +1,71 @@
+using Content.Server.Fluids.EntitySystems;
+using Content.Server.Light.EntitySystems;
+using Content.Server.Spreader;
+using Content.Server.Store.Systems;
+using Content.Shared._DV.Silicons;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Coordinates.Helpers;
+using Content.Shared.Maps;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Map;
+
+namespace Content.Server._DV.Silicons;
+
+public sealed class StationAiShopSystem : SharedStationAiShopSystem
+{
+    [Dependency] private readonly StoreSystem _store = default!;
+    [Dependency] private readonly IMapManager _map = default!;
+    [Dependency] private readonly SmokeSystem _smoke = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+    [Dependency] private readonly SpreaderSystem _spreader = default!;
+    [Dependency] private readonly PoweredLightSystem _poweredLight = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<StationAiShopComponent, StationAiShopActionEvent>(OnShopAction);
+
+        SubscribeLocalEvent<StationAiShopComponent, StationAiLightSynthesizerActionEvent>(OnLightSynthesizer);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiSmokeActionEvent>(OnEmergencySealant);
+    }
+
+    private void OnShopAction(Entity<StationAiShopComponent> ent, ref StationAiShopActionEvent args)
+    {
+        _store.ToggleUi(args.Performer, ent);
+    }
+
+    private void OnLightSynthesizer(Entity<StationAiShopComponent> ent, ref StationAiLightSynthesizerActionEvent args)
+    {
+        if (_poweredLight.EjectBulb(args.Target) is { } oldBulb)
+            Del(oldBulb);
+
+        var bulb = Spawn(args.BulbPrototype);
+        if (!_poweredLight.InsertBulb(args.Target, bulb))
+        {
+            Del(bulb);
+            return;
+        }
+
+        args.Handled = true;
+    }
+
+    private void OnEmergencySealant(Entity<StationAiShopComponent> ent, ref StationAiSmokeActionEvent args)
+    {
+        var mapCoords = _transform.ToMapCoordinates(args.Target);
+        if (!_map.TryFindGridAt(mapCoords, out _, out var grid) ||
+            !grid.TryGetTileRef(args.Target, out var tileRef) ||
+            tileRef.Tile.IsEmpty)
+        {
+            return;
+        }
+
+        if (_spreader.RequiresFloorToSpread(args.SmokePrototype.ToString()) && tileRef.Tile.IsSpace())
+            return;
+
+        var coords = grid.MapToGrid(mapCoords);
+        var uid = Spawn(args.SmokePrototype, coords.SnapToGrid());
+        _smoke.StartSmoke(uid, args.Solution, args.Duration, args.SpreadAmount);
+        args.Handled = true;
+    }
+}
diff --git a/Content.Shared/Store/Components/StoreComponent.cs b/Content.Shared/Store/Components/StoreComponent.cs
index 19d13e6d0528..03498c527b1d 100644
--- a/Content.Shared/Store/Components/StoreComponent.cs
+++ b/Content.Shared/Store/Components/StoreComponent.cs
@@ -73,6 +73,12 @@ public sealed partial class StoreComponent : Component
     [ViewVariables, DataField]
     public bool RefundAllowed;
 
+    /// <summary>
+    /// DeltaV: Should the store grant actions to the mind?
+    /// </summary>
+    [DataField]
+    public bool GrantActionsToMind = true;
+
     /// <summary>
     ///     Checks if store can be opened by the account owner only.
     ///     Not meant to be used with uplinks.
diff --git a/Content.Shared/_DV/Silicons/SharedStationAiShopSystem.cs b/Content.Shared/_DV/Silicons/SharedStationAiShopSystem.cs
new file mode 100644
index 000000000000..dd880d5a7af0
--- /dev/null
+++ b/Content.Shared/_DV/Silicons/SharedStationAiShopSystem.cs
@@ -0,0 +1,68 @@
+using Content.Shared.Actions;
+using Content.Shared.Damage;
+using Content.Shared.Light.Components;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Timing;
+
+namespace Content.Shared._DV.Silicons;
+
+public abstract class SharedStationAiShopSystem : EntitySystem
+{
+    [Dependency] private readonly DamageableSystem _damage = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    [Dependency] private readonly SharedActionsSystem _actions = default!;
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<StationAiShopComponent, MapInitEvent>(OnMapInit);
+        SubscribeLocalEvent<StationAiShopComponent, ComponentShutdown>(OnShutdown);
+
+        SubscribeLocalEvent<StationAiShopComponent, StationAiRgbLightingActionEvent>(OnRgbLighting);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiPlaySoundActionEvent>(OnPlaySound);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiHealthChangeActionEvent>(OnHealthChange);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiSpawnEntityActionEvent>(OnHolopointer);
+    }
+
+    private void OnMapInit(Entity<StationAiShopComponent> ent, ref MapInitEvent args)
+    {
+        _actions.AddAction(ent, ref ent.Comp.ShopAction, ent.Comp.ShopActionId);
+        Dirty(ent);
+    }
+
+    private void OnShutdown(Entity<StationAiShopComponent> ent, ref ComponentShutdown args)
+    {
+        _actions.RemoveAction(ent, ent.Comp.ShopAction);
+    }
+
+    private void OnRgbLighting(Entity<StationAiShopComponent> ent, ref StationAiRgbLightingActionEvent args)
+    {
+        if (!RemComp<RgbLightControllerComponent>(args.Target))
+            AddComp<RgbLightControllerComponent>(args.Target);
+
+        args.Handled = true;
+    }
+
+    private void OnPlaySound(Entity<StationAiShopComponent> ent, ref StationAiPlaySoundActionEvent args)
+    {
+        _audio.PlayPredicted(args.Sound, args.Target, ent);
+        args.Handled = true;
+    }
+
+    private void OnHealthChange(Entity<StationAiShopComponent> ent, ref StationAiHealthChangeActionEvent args)
+    {
+        _damage.TryChangeDamage(args.Target, args.Damage, origin: ent);
+        args.Handled = true;
+    }
+
+    private void OnHolopointer(Entity<StationAiShopComponent> ent, ref StationAiSpawnEntityActionEvent args)
+    {
+        if (!_timing.IsFirstTimePredicted)
+            return;
+
+        Spawn(args.Entity, args.Target);
+        args.Handled = true;
+    }
+}
diff --git a/Content.Shared/_DV/Silicons/StationAiShopComponent.cs b/Content.Shared/_DV/Silicons/StationAiShopComponent.cs
new file mode 100644
index 000000000000..3e162e34bc29
--- /dev/null
+++ b/Content.Shared/_DV/Silicons/StationAiShopComponent.cs
@@ -0,0 +1,95 @@
+using Content.Shared.Actions;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Damage;
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._DV.Silicons;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class StationAiShopComponent : Component
+{
+    [DataField]
+    public EntProtoId ShopActionId = "ActionStationAiOpenShop";
+
+    [DataField, AutoNetworkedField]
+    public EntityUid? ShopAction;
+}
+
+public sealed partial class StationAiShopActionEvent : InstantActionEvent;
+
+/// <summary>
+/// Toggles the RGB light controller on the given entity
+/// </summary>
+public sealed partial class StationAiRgbLightingActionEvent : EntityTargetActionEvent;
+
+/// <summary>
+/// Replaces the light bulb in the target with the given prototype
+/// </summary>
+public sealed partial class StationAiLightSynthesizerActionEvent : EntityTargetActionEvent
+{
+    [DataField(required: true)]
+    public EntProtoId BulbPrototype;
+}
+
+/// <summary>
+/// Plays the given sound coming from the target entity
+/// </summary>
+public sealed partial class StationAiPlaySoundActionEvent : EntityTargetActionEvent
+{
+    [DataField(required: true)]
+    public SoundSpecifier Sound;
+}
+
+/// <summary>
+/// Changes the target entity's health by the given damage specifier
+/// </summary>
+public sealed partial class StationAiHealthChangeActionEvent : EntityTargetActionEvent
+{
+    [DataField(required: true)]
+    public DamageSpecifier Damage;
+}
+
+/// <summary>
+/// Spawns the given entity at the target location
+/// </summary>
+public sealed partial class StationAiSpawnEntityActionEvent : WorldTargetActionEvent
+{
+    [DataField(required: true)]
+    public EntProtoId Entity;
+}
+
+/// <summary>
+/// Triggers the given smoke effect at the target location
+/// </summary>
+public sealed partial class StationAiSmokeActionEvent : WorldTargetActionEvent
+{
+    /// <summary>
+    /// How long the smoke stays for, after it has spread.
+    /// </summary>
+    [DataField]
+    public float Duration = 10;
+
+    /// <summary>
+    /// How much the smoke will spread.
+    /// </summary>
+    [DataField(required: true)]
+    public int SpreadAmount;
+
+    /// <summary>
+    /// Smoke entity to spawn.
+    /// Defaults to smoke but you can use foam if you want.
+    /// </summary>
+    [DataField]
+    public EntProtoId<SmokeComponent> SmokePrototype = "Smoke";
+
+    /// <summary>
+    /// Solution to add to each smoke cloud.
+    /// </summary>
+    /// <remarks>
+    /// When using repeating trigger this essentially gets multiplied so dont do anything crazy like omnizine or lexorin.
+    /// </remarks>
+    [DataField]
+    public Solution Solution = new();
+}
diff --git a/Resources/Locale/en-US/_DV/store/ai-catalog.ftl b/Resources/Locale/en-US/_DV/store/ai-catalog.ftl
new file mode 100644
index 000000000000..7c8753786976
--- /dev/null
+++ b/Resources/Locale/en-US/_DV/store/ai-catalog.ftl
@@ -0,0 +1,17 @@
+ai-rgb-lighting-name = RGB Lighting
+ai-rgb-lighting-desc = Adjust a light's color controller to add or remove an RGB effect
+
+ai-light-synthesizer-name = Light Synthesizer
+ai-light-synthesizer-desc = Send some nanites to synthesize a replacement light bulb in a fixture
+
+ai-bike-horn-name = HONK.mp3
+ai-bike-horn-desc = Play an amusing audio clip from a holopad
+
+ai-holopointer-name = Holopointer
+ai-holopointer-desc = Circle an area of interest with a hologram
+
+ai-repair-nanites-name = Repair Nanites
+ai-repair-nanites-desc = Send some nanites to give a cyborg some minor repairs
+
+ai-emergency-sealant-name = Emergency Sealant
+ai-emergency-sealant-desc = Deploy some metal foam to seal breaches
diff --git a/Resources/Locale/en-US/_DV/store/currency.ftl b/Resources/Locale/en-US/_DV/store/currency.ftl
new file mode 100644
index 000000000000..9300fcf6d998
--- /dev/null
+++ b/Resources/Locale/en-US/_DV/store/currency.ftl
@@ -0,0 +1 @@
+store-currency-display-ai-memory = TB
diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml
index 06da05cb7a8e..24fb7462a766 100644
--- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml
+++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml
@@ -56,6 +56,8 @@
         type: SiliconLawBoundUserInterface
       enum.CommunicationsConsoleUiKey.Key:
         type: CommunicationsConsoleBoundUserInterface
+      enum.StoreUiKey.Key: # DeltaV - AI shop system
+        type: StoreBoundUserInterface
   - type: IntrinsicUI
     uis:
       enum.RadarConsoleUiKey.Key:
@@ -83,6 +85,17 @@
     - sprite: Mobs/Silicon/station_ai.rsi
       state: default
   - type: ShowJobIcons
+  # Begin DeltaV - AI Shop System
+  - type: StationAiShop
+  - type: Store
+    categories:
+    - AiAbilities
+    currencyWhitelist:
+    - AiMemory
+    balance:
+      AiMemory: 3
+    grantActionsToMind: false
+  # End DeltaV - AI Shop System
 
 - type: entity
   id: AiHeldIntellicard
diff --git a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml
index b1f87a3cd789..b5f9206fac47 100644
--- a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml
+++ b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml
@@ -10,6 +10,9 @@
   description: "An always powered light."
   suffix: Always powered
   components:
+  - type: Tag # DeltaV - we need this for an action whitelist
+    tags:
+    - LightFixture
   - type: MeleeSound
     soundGroups:
       Brute:
diff --git a/Resources/Prototypes/_DV/Actions/station_ai.yml b/Resources/Prototypes/_DV/Actions/station_ai.yml
new file mode 100644
index 000000000000..fd9258aba3d3
--- /dev/null
+++ b/Resources/Prototypes/_DV/Actions/station_ai.yml
@@ -0,0 +1,154 @@
+# Actions
+- type: entity
+  id: ActionStationAiOpenShop
+  name: Choose programs
+  description: Configure your installed programs
+  components:
+  - type: InstantAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: store
+    event: !type:StationAiShopActionEvent
+
+- type: entity
+  id: ActionStationAiRgbLighting
+  name: ai-rgb-lighting-name
+  description: ai-rgb-lighting-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      tags:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: rgb_lighting
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 30
+    event: !type:StationAiRgbLightingActionEvent
+
+- type: entity
+  id: ActionStationAiLightSynthesizer
+  name: ai-light-synthesizer-name
+  description: ai-light-synthesizer-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      tags:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: light_synthesizer
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 300
+    event: !type:StationAiLightSynthesizerActionEvent
+      bulbPrototype: LightTube
+
+- type: entity
+  id: ActionStationAiBikeHorn
+  name: ai-bike-horn-name
+  description: ai-bike-horn-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - Holopad
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: bike_horn
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 60
+    event: !type:StationAiPlaySoundActionEvent
+      sound:
+        collection: BikeHorn
+        params:
+          variation: 0.125
+
+- type: entity
+  id: ActionStationAiHoloPointer
+  name: ai-holopointer-name
+  description: ai-holopointer-desc
+  components:
+  - type: WorldTargetAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: holopointer
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 15
+    event: !type:StationAiSpawnEntityActionEvent
+      entity: StationAiHolopointer
+
+- type: entity
+  id: StationAiHolopointer
+  name: holopointer
+  description: A hologram projected by the station's AI to bring your attention to something.
+  components:
+  - type: Transform
+    anchored: true
+  - type: Physics
+    bodyType: Static
+    canCollide: false
+  - type: Sprite
+    sprite: _DV/Objects/Misc/holopointer.rsi
+    state: target_circle
+  - type: TimedDespawn
+    lifetime: 15
+  - type: PointLight
+    enabled: true
+    radius: 1
+    color: red
+  - type: Clickable
+
+- type: entity
+  id: ActionStationAiRepairNanites
+  name: ai-repair-nanites-name
+  description: ai-repair-nanites-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - BorgChassis
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: nanites
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 120
+    event: !type:StationAiHealthChangeActionEvent
+      damage:
+        groups:
+          Burn: -20
+          Brute: -20
+
+- type: entity
+  id: ActionStationAiEmergencySealant
+  name: ai-emergency-sealant-name
+  description: ai-emergency-sealant-desc
+  components:
+  - type: WorldTargetAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: emergency_sealant
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 300
+    event: !type:StationAiSmokeActionEvent
+      duration: 10
+      spreadAmount: 8
+      smokePrototype: AluminiumMetalFoam
diff --git a/Resources/Prototypes/_DV/Catalog/ai_catalog.yml b/Resources/Prototypes/_DV/Catalog/ai_catalog.yml
new file mode 100644
index 000000000000..21166d99cec0
--- /dev/null
+++ b/Resources/Prototypes/_DV/Catalog/ai_catalog.yml
@@ -0,0 +1,77 @@
+- type: listing
+  id: AiRgbLighting
+  name: ai-rgb-lighting-name
+  description: ai-rgb-lighting-desc
+  productAction: ActionStationAiRgbLighting
+  cost:
+    AiMemory: 1
+  categories:
+  - AiAbilities
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
+
+- type: listing
+  id: AiHolopointer
+  name: ai-holopointer-name
+  description: ai-holopointer-desc
+  productAction: ActionStationAiHoloPointer
+  cost:
+    AiMemory: 1
+  categories:
+  - AiAbilities
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
+
+- type: listing
+  id: AiLightSynthesizer
+  name: ai-light-synthesizer-name
+  description: ai-light-synthesizer-desc
+  productAction: ActionStationAiLightSynthesizer
+  cost:
+    AiMemory: 1
+  categories:
+  - AiAbilities
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
+
+- type: listing
+  id: AiBikeHorn
+  name: ai-bike-horn-name
+  description: ai-bike-horn-desc
+  productAction: ActionStationAiBikeHorn
+  cost:
+    AiMemory: 1
+  categories:
+  - AiAbilities
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
+
+- type: listing
+  id: AiEmergencySealant
+  name: ai-emergency-sealant-name
+  description: ai-emergency-sealant-desc
+  productAction: ActionStationAiEmergencySealant
+  cost:
+    AiMemory: 2
+  categories:
+  - AiAbilities
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
+
+- type: listing
+  id: AiRepairNanites
+  name: ai-repair-nanites-name
+  description: ai-repair-nanites-desc
+  productAction: ActionStationAiRepairNanites
+  cost:
+    AiMemory: 2
+  categories:
+  - AiAbilities
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
diff --git a/Resources/Prototypes/_DV/Store/categories.yml b/Resources/Prototypes/_DV/Store/categories.yml
index c19d42a60f8f..ad10bfe34c21 100644
--- a/Resources/Prototypes/_DV/Store/categories.yml
+++ b/Resources/Prototypes/_DV/Store/categories.yml
@@ -2,3 +2,7 @@
   id: UplinkObjectives
   name: store-category-objectives
   priority: 11
+
+- type: storeCategory
+  id: AiAbilities
+  name: store-category-abilities
diff --git a/Resources/Prototypes/_DV/Store/currency.yml b/Resources/Prototypes/_DV/Store/currency.yml
new file mode 100644
index 000000000000..a618bf84b607
--- /dev/null
+++ b/Resources/Prototypes/_DV/Store/currency.yml
@@ -0,0 +1,4 @@
+- type: currency
+  id: AiMemory
+  displayName: store-currency-display-ai-memory
+  canWithdraw: false
diff --git a/Resources/Prototypes/_DV/tags.yml b/Resources/Prototypes/_DV/tags.yml
index afb09d7c0566..6c3d7257edef 100644
--- a/Resources/Prototypes/_DV/tags.yml
+++ b/Resources/Prototypes/_DV/tags.yml
@@ -96,6 +96,9 @@
 - type: Tag # prevents ID chips being used for an id computer privileged id
   id: IdChip
 
+- type: Tag
+  id: LightFixture
+
 - type: Tag
   id: MagazineLaser
 
diff --git a/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/bike_horn.png b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/bike_horn.png
new file mode 100644
index 000000000000..dc44770dd778
Binary files /dev/null and b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/bike_horn.png differ
diff --git a/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/emergency_sealant.png b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/emergency_sealant.png
new file mode 100644
index 000000000000..1f85015dec9c
Binary files /dev/null and b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/emergency_sealant.png differ
diff --git a/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/holopointer.png b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/holopointer.png
new file mode 100644
index 000000000000..ac00bca6069e
Binary files /dev/null and b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/holopointer.png differ
diff --git a/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/light_synthesizer.png b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/light_synthesizer.png
new file mode 100644
index 000000000000..a3f33c38a586
Binary files /dev/null and b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/light_synthesizer.png differ
diff --git a/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/meta.json b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/meta.json
new file mode 100644
index 000000000000..31e1c0e7d315
--- /dev/null
+++ b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/meta.json
@@ -0,0 +1,32 @@
+{
+    "version": 1,
+    "license": "CC-BY-SA-3.0",
+    "copyright": "Janet Blackquill <uhhadd@gmail.com> 2025",
+    "size": {
+        "x": 32,
+        "y": 32
+    },
+    "states": [
+        {
+            "name": "bike_horn"
+        },
+        {
+            "name": "emergency_sealant"
+        },
+        {
+            "name": "holopointer"
+        },
+        {
+            "name": "light_synthesizer"
+        },
+        {
+            "name": "nanites"
+        },
+        {
+            "name": "rgb_lighting"
+        },
+        {
+            "name": "store"
+        }
+    ]
+}
diff --git a/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/nanites.png b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/nanites.png
new file mode 100644
index 000000000000..6055123cc537
Binary files /dev/null and b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/nanites.png differ
diff --git a/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/rgb_lighting.png b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/rgb_lighting.png
new file mode 100644
index 000000000000..7e917c0f251e
Binary files /dev/null and b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/rgb_lighting.png differ
diff --git a/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/store.png b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/store.png
new file mode 100644
index 000000000000..d42f89619fe1
Binary files /dev/null and b/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/store.png differ
diff --git a/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/meta.json b/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/meta.json
new file mode 100644
index 000000000000..003c495a3e96
--- /dev/null
+++ b/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/meta.json
@@ -0,0 +1,41 @@
+{
+    "version": 1,
+    "license": "CC-BY-SA-3.0",
+    "copyright": "Taken from Paradise Station at https://github.com/ParadiseSS13/Paradise/blob/a126858e60caebc7c3a4b25400a5969dcc63f092/icons/mob/telegraphing/telegraph_holographic.dmi",
+    "size": {
+        "x": 32,
+        "y": 32
+    },
+    "states": [
+        {
+            "name": "target_box",
+            "delays": [
+                [
+                    0.125,
+                    0.125,
+                    0.125,
+                    0.125,
+                    0.125,
+                    0.125,
+                    0.125,
+                    0.125
+                ]
+            ]
+        },
+        {
+            "name": "target_circle",
+            "delays": [
+                [
+                    0.125,
+                    0.125,
+                    0.125,
+                    0.125,
+                    0.125,
+                    0.125,
+                    0.125,
+                    0.125
+                ]
+            ]
+        }
+    ]
+}
diff --git a/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/target_box.png b/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/target_box.png
new file mode 100644
index 000000000000..e0f0f9ecd2b2
Binary files /dev/null and b/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/target_box.png differ
diff --git a/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/target_circle.png b/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/target_circle.png
new file mode 100644
index 000000000000..3836ed3ab5f2
Binary files /dev/null and b/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/target_circle.png differ


Reviews:
user: login: deltanedas
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T10:40:53Z
user: login: sowelipililimute
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T15:43:09Z
user: login: sowelipililimute
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T15:44:53Z
user: login: sowelipililimute
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T15:45:19Z
user: login: deltanedas
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T16:36:15Z
user: login: darklyac
body: Direction approved! ;)
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T16:11:09Z
user: login: deltanedas
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T20:35:29Z
user: login: deltanedas
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T20:50:06Z

Review comments:
diff_hunk: @@ -73,6 +73,12 @@ public sealed partial class StoreComponent : Component
     [ViewVariables, DataField]
     public bool RefundAllowed;
 
+    /// <summary>
+    ///     DeltaV: Should the store grant actions to the mind?
path: Content.Shared/Store/Components/StoreComponent.cs
user: login: deltanedas
body: dont indent summaries
created_at: 2025-05-01T10:32:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,64 @@
+using Content.Shared.Actions;
+using Content.Shared.Damage;
+using Content.Shared.Light.Components;
+using Robust.Shared.Audio.Systems;
+
+namespace Content.Shared._DV.Silicons;
+
+public abstract class SharedStationAiShopSystem : EntitySystem
+{
+    [Dependency] private readonly DamageableSystem _damage = default!;
+    [Dependency] private readonly SharedActionsSystem _actions = default!;
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<StationAiShopComponent, MapInitEvent>(OnMapInit);
+        SubscribeLocalEvent<StationAiShopComponent, ComponentShutdown>(OnShutdown);
+
+        SubscribeLocalEvent<StationAiShopComponent, StationAiRgbLightingActionEvent>(OnRgbLighting);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiPlaySoundActionEvent>(OnPlaySound);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiHealthChangeActionEvent>(OnHealthChange);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiSpawnEntityActionEvent>(OnHolopointer);
+    }
+
+    private void OnMapInit(Entity<StationAiShopComponent> ent, ref MapInitEvent args)
+    {
+        _actions.AddAction(ent, ref ent.Comp.ShopAction, ent.Comp.ShopActionId);
path: Content.Shared/_DV/Silicons/SharedStationAiShopSystem.cs
user: login: deltanedas
body: dirty it too
created_at: 2025-05-01T10:33:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -56,6 +56,8 @@
         type: SiliconLawBoundUserInterface
       enum.CommunicationsConsoleUiKey.Key:
         type: CommunicationsConsoleBoundUserInterface
+      enum.StoreUiKey.Key:
path: Resources/Prototypes/Entities/Mobs/Player/silicon.yml
user: login: deltanedas
body: comment
created_at: 2025-05-01T10:35:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -83,6 +85,15 @@
     - sprite: Mobs/Silicon/station_ai.rsi
       state: default
   - type: ShowJobIcons
+  - type: StationAiShop
+  - type: Store
+    categories:
+    - AiAbilities
+    currencyWhitelist:
+    - AiMemory
+    balance:
+      AiMemory: 3
+    grantActionsToMind: false
path: Resources/Prototypes/Entities/Mobs/Player/silicon.yml
user: login: deltanedas
body: begin end comments
created_at: 2025-05-01T10:35:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,6 +10,9 @@
   description: "An always powered light."
   suffix: Always powered
   components:
+  - type: Tag # DeltaV - we need this for an action whitelist
path: Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml
user: login: deltanedas
body: is PoweredLight not in shared
created_at: 2025-05-01T10:36:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,159 @@
+# Actions
+- type: entity
+  id: ActionStationAiOpenShop
+  name: Choose programs
+  description: Configure your installed programs
+  components:
+  - type: InstantAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: store
+    event: !type:StationAiShopActionEvent
+
+- type: entity
+  id: ActionStationAiRgbLighting
+  name: ai-rgb-lighting-name
+  description: ai-rgb-lighting-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      tags:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: rgb_lighting
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 30
+    event: !type:StationAiRgbLightingActionEvent
+
+- type: entity
+  id: ActionStationAiLightSynthesizer
+  name: ai-light-synthesizer-name
+  description: ai-light-synthesizer-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: light_synthesizer
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 300
+    event:
+      !type:StationAiLightSynthesizerActionEvent
path: Resources/Prototypes/_DV/Actions/station_ai.yml
user: login: deltanedas
body: move this to the line above
created_at: 2025-05-01T10:37:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,159 @@
+# Actions
+- type: entity
+  id: ActionStationAiOpenShop
+  name: Choose programs
+  description: Configure your installed programs
+  components:
+  - type: InstantAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: store
+    event: !type:StationAiShopActionEvent
+
+- type: entity
+  id: ActionStationAiRgbLighting
+  name: ai-rgb-lighting-name
+  description: ai-rgb-lighting-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      tags:
+      - LightFixture
path: Resources/Prototypes/_DV/Actions/station_ai.yml
user: login: deltanedas
body: why doesnt this use LightFixture component which is used below
created_at: 2025-05-01T10:38:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,159 @@
+# Actions
+- type: entity
+  id: ActionStationAiOpenShop
+  name: Choose programs
+  description: Configure your installed programs
+  components:
+  - type: InstantAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: store
+    event: !type:StationAiShopActionEvent
+
+- type: entity
+  id: ActionStationAiRgbLighting
+  name: ai-rgb-lighting-name
+  description: ai-rgb-lighting-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      tags:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: rgb_lighting
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 30
+    event: !type:StationAiRgbLightingActionEvent
+
+- type: entity
+  id: ActionStationAiLightSynthesizer
+  name: ai-light-synthesizer-name
+  description: ai-light-synthesizer-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: light_synthesizer
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 300
+    event:
+      !type:StationAiLightSynthesizerActionEvent
+      bulbPrototype: LightTube
+
+- type: entity
+  id: ActionStationAiBikeHorn
+  name: ai-bike-horn-name
+  description: ai-bike-horn-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - Holopad
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: bike_horn
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 60
+    event:
+      !type:StationAiPlaySoundActionEvent
path: Resources/Prototypes/_DV/Actions/station_ai.yml
user: login: deltanedas
body: .
created_at: 2025-05-01T10:38:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,159 @@
+# Actions
+- type: entity
+  id: ActionStationAiOpenShop
+  name: Choose programs
+  description: Configure your installed programs
+  components:
+  - type: InstantAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: store
+    event: !type:StationAiShopActionEvent
+
+- type: entity
+  id: ActionStationAiRgbLighting
+  name: ai-rgb-lighting-name
+  description: ai-rgb-lighting-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      tags:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: rgb_lighting
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 30
+    event: !type:StationAiRgbLightingActionEvent
+
+- type: entity
+  id: ActionStationAiLightSynthesizer
+  name: ai-light-synthesizer-name
+  description: ai-light-synthesizer-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: light_synthesizer
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 300
+    event:
+      !type:StationAiLightSynthesizerActionEvent
+      bulbPrototype: LightTube
+
+- type: entity
+  id: ActionStationAiBikeHorn
+  name: ai-bike-horn-name
+  description: ai-bike-horn-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - Holopad
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: bike_horn
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 60
+    event:
+      !type:StationAiPlaySoundActionEvent
+      sound:
+        collection: BikeHorn
+        params:
+          variation: 0.125
+
+- type: entity
+  id: ActionStationAiHoloPointer
+  name: ai-holopointer-name
+  description: ai-holopointer-desc
+  components:
+  - type: WorldTargetAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: holopointer
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 15
+    event:
+      !type:StationAiSpawnEntityActionEvent
path: Resources/Prototypes/_DV/Actions/station_ai.yml
user: login: deltanedas
body: .
created_at: 2025-05-01T10:38:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,159 @@
+# Actions
+- type: entity
+  id: ActionStationAiOpenShop
+  name: Choose programs
+  description: Configure your installed programs
+  components:
+  - type: InstantAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: store
+    event: !type:StationAiShopActionEvent
+
+- type: entity
+  id: ActionStationAiRgbLighting
+  name: ai-rgb-lighting-name
+  description: ai-rgb-lighting-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      tags:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: rgb_lighting
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 30
+    event: !type:StationAiRgbLightingActionEvent
+
+- type: entity
+  id: ActionStationAiLightSynthesizer
+  name: ai-light-synthesizer-name
+  description: ai-light-synthesizer-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: light_synthesizer
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 300
+    event:
+      !type:StationAiLightSynthesizerActionEvent
+      bulbPrototype: LightTube
+
+- type: entity
+  id: ActionStationAiBikeHorn
+  name: ai-bike-horn-name
+  description: ai-bike-horn-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - Holopad
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: bike_horn
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 60
+    event:
+      !type:StationAiPlaySoundActionEvent
+      sound:
+        collection: BikeHorn
+        params:
+          variation: 0.125
+
+- type: entity
+  id: ActionStationAiHoloPointer
+  name: ai-holopointer-name
+  description: ai-holopointer-desc
+  components:
+  - type: WorldTargetAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: holopointer
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 15
+    event:
+      !type:StationAiSpawnEntityActionEvent
+      entity: StationAiHolopointer
+
+- type: entity
+  id: StationAiHolopointer
+  name: holopointer
+  description: A hologram projected by the station's AI to bring your attention to something.
+  components:
+  - type: Transform
+    anchored: true
+  - type: Physics
+    bodyType: Static
+    canCollide: false
+  - type: Sprite
+    sprite: _DV/Objects/Misc/holopointer.rsi
+    state: target_circle
+  - type: TimedDespawn
+    lifetime: 15
+  - type: PointLight
+    enabled: true
+    radius: 1
+    color: red
+  - type: Clickable
+
+- type: entity
+  id: ActionStationAiRepairNanites
+  name: ai-repair-nanites-name
+  description: ai-repair-nanites-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - BorgChassis
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: nanites
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 120
+    event:
+      !type:StationAiHealthChangeActionEvent
path: Resources/Prototypes/_DV/Actions/station_ai.yml
user: login: deltanedas
body: .
created_at: 2025-05-01T10:39:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,159 @@
+# Actions
+- type: entity
+  id: ActionStationAiOpenShop
+  name: Choose programs
+  description: Configure your installed programs
+  components:
+  - type: InstantAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: store
+    event: !type:StationAiShopActionEvent
+
+- type: entity
+  id: ActionStationAiRgbLighting
+  name: ai-rgb-lighting-name
+  description: ai-rgb-lighting-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      tags:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: rgb_lighting
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 30
+    event: !type:StationAiRgbLightingActionEvent
+
+- type: entity
+  id: ActionStationAiLightSynthesizer
+  name: ai-light-synthesizer-name
+  description: ai-light-synthesizer-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - LightFixture
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: light_synthesizer
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 300
+    event:
+      !type:StationAiLightSynthesizerActionEvent
+      bulbPrototype: LightTube
+
+- type: entity
+  id: ActionStationAiBikeHorn
+  name: ai-bike-horn-name
+  description: ai-bike-horn-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - Holopad
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: bike_horn
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 60
+    event:
+      !type:StationAiPlaySoundActionEvent
+      sound:
+        collection: BikeHorn
+        params:
+          variation: 0.125
+
+- type: entity
+  id: ActionStationAiHoloPointer
+  name: ai-holopointer-name
+  description: ai-holopointer-desc
+  components:
+  - type: WorldTargetAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: holopointer
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 15
+    event:
+      !type:StationAiSpawnEntityActionEvent
+      entity: StationAiHolopointer
+
+- type: entity
+  id: StationAiHolopointer
+  name: holopointer
+  description: A hologram projected by the station's AI to bring your attention to something.
+  components:
+  - type: Transform
+    anchored: true
+  - type: Physics
+    bodyType: Static
+    canCollide: false
+  - type: Sprite
+    sprite: _DV/Objects/Misc/holopointer.rsi
+    state: target_circle
+  - type: TimedDespawn
+    lifetime: 15
+  - type: PointLight
+    enabled: true
+    radius: 1
+    color: red
+  - type: Clickable
+
+- type: entity
+  id: ActionStationAiRepairNanites
+  name: ai-repair-nanites-name
+  description: ai-repair-nanites-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      components:
+      - BorgChassis
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: nanites
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 120
+    event:
+      !type:StationAiHealthChangeActionEvent
+      damage:
+        groups:
+          Burn: -20
+          Brute: -20
+
+- type: entity
+  id: ActionStationAiEmergencySealant
+  name: ai-emergency-sealant-name
+  description: ai-emergency-sealant-desc
+  components:
+  - type: WorldTargetAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: emergency_sealant
+    range: -1
+    checkCanAccess: false
+    checkCanInteract: false
+    useDelay: 300
+    event:
+      !type:StationAiSmokeActionEvent
path: Resources/Prototypes/_DV/Actions/station_ai.yml
user: login: deltanedas
body: .
created_at: 2025-05-01T10:39:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,159 @@
+# Actions
+- type: entity
+  id: ActionStationAiOpenShop
+  name: Choose programs
+  description: Configure your installed programs
+  components:
+  - type: InstantAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: store
+    event: !type:StationAiShopActionEvent
+
+- type: entity
+  id: ActionStationAiRgbLighting
+  name: ai-rgb-lighting-name
+  description: ai-rgb-lighting-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      tags:
+      - LightFixture
path: Resources/Prototypes/_DV/Actions/station_ai.yml
user: login: sowelipililimute
body: the component shared by wall lights is server-only unfortunately so the client can't use it as a whitelist
created_at: 2025-05-01T15:43:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,6 +10,9 @@
   description: "An always powered light."
   suffix: Always powered
   components:
+  - type: Tag # DeltaV - we need this for an action whitelist
path: Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml
user: login: sowelipililimute
body: yes
created_at: 2025-05-01T15:44:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,159 @@
+# Actions
+- type: entity
+  id: ActionStationAiOpenShop
+  name: Choose programs
+  description: Configure your installed programs
+  components:
+  - type: InstantAction
+    itemIconStyle: BigAction
+    icon:
+      sprite: _DV/Interface/Actions/actions_ai.rsi
+      state: store
+    event: !type:StationAiShopActionEvent
+
+- type: entity
+  id: ActionStationAiRgbLighting
+  name: ai-rgb-lighting-name
+  description: ai-rgb-lighting-desc
+  components:
+  - type: EntityTargetAction
+    whitelist:
+      tags:
+      - LightFixture
path: Resources/Prototypes/_DV/Actions/station_ai.yml
user: login: sowelipililimute
body: also typo
created_at: 2025-05-01T15:45:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,65 @@
+using Content.Shared.Actions;
+using Content.Shared.Damage;
+using Content.Shared.Light.Components;
+using Robust.Shared.Audio.Systems;
+
+namespace Content.Shared._DV.Silicons;
+
+public abstract class SharedStationAiShopSystem : EntitySystem
+{
+    [Dependency] private readonly DamageableSystem _damage = default!;
+    [Dependency] private readonly SharedActionsSystem _actions = default!;
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<StationAiShopComponent, MapInitEvent>(OnMapInit);
+        SubscribeLocalEvent<StationAiShopComponent, ComponentShutdown>(OnShutdown);
+
+        SubscribeLocalEvent<StationAiShopComponent, StationAiRgbLightingActionEvent>(OnRgbLighting);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiPlaySoundActionEvent>(OnPlaySound);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiHealthChangeActionEvent>(OnHealthChange);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiSpawnEntityActionEvent>(OnHolopointer);
+    }
+
+    private void OnMapInit(Entity<StationAiShopComponent> ent, ref MapInitEvent args)
+    {
+        _actions.AddAction(ent, ref ent.Comp.ShopAction, ent.Comp.ShopActionId);
+        Dirty(ent);
+    }
+
+    private void OnShutdown(Entity<StationAiShopComponent> ent, ref ComponentShutdown args)
+    {
+        _actions.RemoveAction(ent, ent.Comp.ShopAction);
+    }
+
+    private void OnRgbLighting(Entity<StationAiShopComponent> ent, ref StationAiRgbLightingActionEvent args)
+    {
+        if (HasComp<RgbLightControllerComponent>(args.Target))
+            RemComp<RgbLightControllerComponent>(args.Target);
path: Content.Shared/_DV/Silicons/SharedStationAiShopSystem.cs
user: login: deltanedas
body: use !RemComp instead of hascomp
created_at: 2025-05-01T16:33:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,65 @@
+using Content.Shared.Actions;
+using Content.Shared.Damage;
+using Content.Shared.Light.Components;
+using Robust.Shared.Audio.Systems;
+
+namespace Content.Shared._DV.Silicons;
+
+public abstract class SharedStationAiShopSystem : EntitySystem
+{
+    [Dependency] private readonly DamageableSystem _damage = default!;
+    [Dependency] private readonly SharedActionsSystem _actions = default!;
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<StationAiShopComponent, MapInitEvent>(OnMapInit);
+        SubscribeLocalEvent<StationAiShopComponent, ComponentShutdown>(OnShutdown);
+
+        SubscribeLocalEvent<StationAiShopComponent, StationAiRgbLightingActionEvent>(OnRgbLighting);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiPlaySoundActionEvent>(OnPlaySound);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiHealthChangeActionEvent>(OnHealthChange);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiSpawnEntityActionEvent>(OnHolopointer);
+    }
+
+    private void OnMapInit(Entity<StationAiShopComponent> ent, ref MapInitEvent args)
+    {
+        _actions.AddAction(ent, ref ent.Comp.ShopAction, ent.Comp.ShopActionId);
+        Dirty(ent);
+    }
+
+    private void OnShutdown(Entity<StationAiShopComponent> ent, ref ComponentShutdown args)
+    {
+        _actions.RemoveAction(ent, ent.Comp.ShopAction);
+    }
+
+    private void OnRgbLighting(Entity<StationAiShopComponent> ent, ref StationAiRgbLightingActionEvent args)
+    {
+        if (HasComp<RgbLightControllerComponent>(args.Target))
+            RemComp<RgbLightControllerComponent>(args.Target);
+        else
+            AddComp<RgbLightControllerComponent>(args.Target);
+
+        args.Handled = true;
+    }
+
+    private void OnPlaySound(Entity<StationAiShopComponent> ent, ref StationAiPlaySoundActionEvent args)
+    {
+        _audio.PlayPredicted(args.Sound, args.Target, ent);
+        args.Handled = true;
+    }
+
+    private void OnHealthChange(Entity<StationAiShopComponent> ent, ref StationAiHealthChangeActionEvent args)
+    {
+        _damage.TryChangeDamage(args.Target, args.Damage);
path: Content.Shared/_DV/Silicons/SharedStationAiShopSystem.cs
user: login: deltanedas
body: pass ent for source
created_at: 2025-05-01T16:33:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,65 @@
+using Content.Shared.Actions;
+using Content.Shared.Damage;
+using Content.Shared.Light.Components;
+using Robust.Shared.Audio.Systems;
+
+namespace Content.Shared._DV.Silicons;
+
+public abstract class SharedStationAiShopSystem : EntitySystem
+{
+    [Dependency] private readonly DamageableSystem _damage = default!;
+    [Dependency] private readonly SharedActionsSystem _actions = default!;
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<StationAiShopComponent, MapInitEvent>(OnMapInit);
+        SubscribeLocalEvent<StationAiShopComponent, ComponentShutdown>(OnShutdown);
+
+        SubscribeLocalEvent<StationAiShopComponent, StationAiRgbLightingActionEvent>(OnRgbLighting);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiPlaySoundActionEvent>(OnPlaySound);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiHealthChangeActionEvent>(OnHealthChange);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiSpawnEntityActionEvent>(OnHolopointer);
+    }
+
+    private void OnMapInit(Entity<StationAiShopComponent> ent, ref MapInitEvent args)
+    {
+        _actions.AddAction(ent, ref ent.Comp.ShopAction, ent.Comp.ShopActionId);
+        Dirty(ent);
+    }
+
+    private void OnShutdown(Entity<StationAiShopComponent> ent, ref ComponentShutdown args)
+    {
+        _actions.RemoveAction(ent, ent.Comp.ShopAction);
+    }
+
+    private void OnRgbLighting(Entity<StationAiShopComponent> ent, ref StationAiRgbLightingActionEvent args)
+    {
+        if (HasComp<RgbLightControllerComponent>(args.Target))
+            RemComp<RgbLightControllerComponent>(args.Target);
+        else
+            AddComp<RgbLightControllerComponent>(args.Target);
+
+        args.Handled = true;
+    }
+
+    private void OnPlaySound(Entity<StationAiShopComponent> ent, ref StationAiPlaySoundActionEvent args)
+    {
+        _audio.PlayPredicted(args.Sound, args.Target, ent);
+        args.Handled = true;
+    }
+
+    private void OnHealthChange(Entity<StationAiShopComponent> ent, ref StationAiHealthChangeActionEvent args)
+    {
+        _damage.TryChangeDamage(args.Target, args.Damage);
+        args.Handled = true;
+    }
+
+    private void OnHolopointer(Entity<StationAiShopComponent> ent, ref StationAiSpawnEntityActionEvent args)
+    {
+        Spawn(args.Entity, args.Target);
path: Content.Shared/_DV/Silicons/SharedStationAiShopSystem.cs
user: login: deltanedas
body: will spawn 20 of them check IsFirstTimePredicted
created_at: 2025-05-01T16:34:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,78 @@
+using Content.Server.Fluids.EntitySystems;
+using Content.Server.Light.EntitySystems;
+using Content.Server.Spreader;
+using Content.Server.Store.Systems;
+using Content.Shared._DV.Silicons;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Coordinates.Helpers;
+using Content.Shared.Maps;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Map;
+
+namespace Content.Server._DV.Silicons;
+
+public sealed class StationAiShopSystem : SharedStationAiShopSystem
+{
+    [Dependency] private readonly StoreSystem _store = default!;
+    [Dependency] private readonly IMapManager _map = default!;
+    [Dependency] private readonly SmokeSystem _smoke = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+    [Dependency] private readonly SpreaderSystem _spreader = default!;
+    [Dependency] private readonly PoweredLightSystem _poweredLight = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<StationAiShopComponent, StationAiShopActionEvent>(OnShopAction);
+
+        SubscribeLocalEvent<StationAiShopComponent, StationAiLightSynthesizerActionEvent>(OnLightSynthesizer);
+        SubscribeLocalEvent<StationAiShopComponent, StationAiSmokeActionEvent>(OnEmergencySealant);
+    }
+
+    private void OnShopAction(Entity<StationAiShopComponent> ent, ref StationAiShopActionEvent args)
+    {
+        _store.ToggleUi(args.Performer, ent);
+    }
+
+    private void OnLightSynthesizer(Entity<StationAiShopComponent> ent, ref StationAiLightSynthesizerActionEvent args)
+    {
+        if (_poweredLight.EjectBulb(args.Target) is { } oldBulb)
+            Del(oldBulb);
+
+        var bulb = Spawn(args.BulbPrototype);
+        if (!_poweredLight.InsertBulb(args.Target, bulb))
+        {
+            Del(bulb);
+            return;
+        }
+
+        args.Handled = true;
+    }
+
+    private void OnEmergencySealant(Entity<StationAiShopComponent> ent, ref StationAiSmokeActionEvent args)
+    {
+        var mapCoords = _transform.ToMapCoordinates(args.Target);
+        if (!_map.TryFindGridAt(mapCoords, out _, out var grid) ||
+            !grid.TryGetTileRef(args.Target, out var tileRef) ||
+            tileRef.Tile.IsEmpty)
+        {
+            return;
+        }
+
+        if (_spreader.RequiresFloorToSpread(args.SmokePrototype.ToString()) && tileRef.Tile.IsSpace())
+            return;
+
+        var coords = grid.MapToGrid(mapCoords);
+        var uid = Spawn(args.SmokePrototype, coords.SnapToGrid());
+        if (!TryComp<SmokeComponent>(uid, out var smoke))
+        {
+            Log.Error($"Smoke prototype {args.SmokePrototype} was missing SmokeComponent");
+            Del(uid);
+            return;
+        }
path: Content.Server/_DV/Silicons/StationAiShopSystem.cs
user: login: deltanedas
body: use `EntProtoId<SmokeComponent>` instead of reinventing the wheel, dont need to pass component its resolved anyway
created_at: 2025-05-02T20:31:47Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-01T04:58:35Z
body: RSI Diff Bot; head commit fe2d57d0826b3be2bdd0c05446030cbb539b99f5 merging into dad088edcb8dba306b0b25148779df42107cd02d
This PR makes changes to 1 or more RSIs. Here is a summary of all changes:

<details><summary>Resources/Textures/_DV/Interface/Actions/actions_ai.rsi</summary>
<p>

| State | Old | New | Status
| --- | --- | --- | --- |
| bike_horn | ![]() | ![](https://raw.githubusercontent.com/sowelipililimute/Delta-v/fe2d57d0826b3be2bdd0c05446030cbb539b99f5/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/bike_horn.png) | Added
| emergency_sealant | ![]() | ![](https://raw.githubusercontent.com/sowelipililimute/Delta-v/fe2d57d0826b3be2bdd0c05446030cbb539b99f5/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/emergency_sealant.png) | Added
| holopointer | ![]() | ![](https://raw.githubusercontent.com/sowelipililimute/Delta-v/fe2d57d0826b3be2bdd0c05446030cbb539b99f5/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/holopointer.png) | Added
| light_synthesizer | ![]() | ![](https://raw.githubusercontent.com/sowelipililimute/Delta-v/fe2d57d0826b3be2bdd0c05446030cbb539b99f5/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/light_synthesizer.png) | Added
| nanites | ![]() | ![](https://raw.githubusercontent.com/sowelipililimute/Delta-v/fe2d57d0826b3be2bdd0c05446030cbb539b99f5/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/nanites.png) | Added
| rgb_lighting | ![]() | ![](https://raw.githubusercontent.com/sowelipililimute/Delta-v/fe2d57d0826b3be2bdd0c05446030cbb539b99f5/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/rgb_lighting.png) | Added
| store | ![]() | ![](https://raw.githubusercontent.com/sowelipililimute/Delta-v/fe2d57d0826b3be2bdd0c05446030cbb539b99f5/Resources/Textures/_DV/Interface/Actions/actions_ai.rsi/store.png) | Added


</p>
</details><details><summary>Resources/Textures/_DV/Objects/Misc/holopointer.rsi</summary>
<p>

| State | Old | New | Status
| --- | --- | --- | --- |
| target_box | ![]() | ![](https://raw.githubusercontent.com/sowelipililimute/Delta-v/fe2d57d0826b3be2bdd0c05446030cbb539b99f5/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/target_box.png) | Added
| target_circle | ![]() | ![](https://raw.githubusercontent.com/sowelipililimute/Delta-v/fe2d57d0826b3be2bdd0c05446030cbb539b99f5/Resources/Textures/_DV/Objects/Misc/holopointer.rsi/target_circle.png) | Added


</p>
</details>
Edit: diff updated after fe2d57d0826b3be2bdd0c05446030cbb539b99f5
author_association: CONTRIBUTOR
user: login: darklyac
created_at: 2025-05-01T15:09:10Z
body: Under direction review ;)
author_association: CONTRIBUTOR