--- /dev/null
+using Content.Shared.Silicons.StationAi;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Silicons.StationAi;
+
+public sealed class StationAiCustomizationBoundUserInterface : BoundUserInterface
+{
+ private StationAiCustomizationMenu? _menu;
+
+ public StationAiCustomizationBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _menu = new StationAiCustomizationMenu(Owner);
+ _menu.OpenCentered();
+ _menu.OnClose += Close;
+
+ _menu.SendStationAiCustomizationMessageAction += SendStationAiCustomizationMessage;
+ }
+
+ public void SendStationAiCustomizationMessage(ProtoId<StationAiCustomizationGroupPrototype> groupProtoId, ProtoId<StationAiCustomizationPrototype> customizationProtoId)
+ {
+ SendPredictedMessage(new StationAiCustomizationMessage(groupProtoId, customizationProtoId));
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ if (!disposing)
+ return;
+
+ _menu?.Dispose();
+ }
+}
--- /dev/null
+<controls:FancyWindow xmlns="https://spacestation14.io"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
+ xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
+ SetSize="600 600"
+ MinSize="600 220">
+ <BoxContainer Orientation="Vertical" VerticalExpand="True">
+ <BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="10 5 10 -5">
+ <controls:StripeBack HorizontalExpand="True">
+ <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
+ <Control SetWidth="200">
+ <Label Text="{Loc 'station-ai-customization-categories'}" Margin="0 5 0 5" HorizontalExpand="True" HorizontalAlignment="Center" />
+ </Control>
+ <Label Text="{Loc 'station-ai-customization-options'}" Margin="0 5 0 5" HorizontalExpand="True" HorizontalAlignment="Center"/>
+ </BoxContainer>
+ </controls:StripeBack>
+ </BoxContainer>
+ <VerticalTabContainer Name="CustomizationGroupsContainer"
+ VerticalExpand="True"
+ HorizontalExpand="True">
+ </VerticalTabContainer>
+ </BoxContainer>
+</controls:FancyWindow>
--- /dev/null
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Silicons.StationAi;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+using System.Linq;
+using System.Numerics;
+
+namespace Content.Client.Silicons.StationAi;
+
+[GenerateTypedNameReferences]
+public sealed partial class StationAiCustomizationMenu : FancyWindow
+{
+ [Dependency] private readonly IEntityManager _entManager = default!;
+ [Dependency] private readonly IPrototypeManager _protoManager = default!;
+
+ private Dictionary<ProtoId<StationAiCustomizationGroupPrototype>, StationAiCustomizationGroupContainer> _groupContainers = new();
+ private Dictionary<ProtoId<StationAiCustomizationGroupPrototype>, ButtonGroup> _buttonGroups = new();
+
+ public event Action<ProtoId<StationAiCustomizationGroupPrototype>, ProtoId<StationAiCustomizationPrototype>>? SendStationAiCustomizationMessageAction;
+
+ private const float IconScale = 1.75f;
+
+ public StationAiCustomizationMenu(EntityUid owner)
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ var stationAiSystem = _entManager.System<SharedStationAiSystem>();
+
+ // Load customziation data
+ _entManager.TryGetComponent<StationAiCoreComponent>(owner, out var stationAiCore);
+ stationAiSystem.TryGetHeld((owner, stationAiCore), out var insertedAi);
+ _entManager.TryGetComponent<StationAiCustomizationComponent>(insertedAi, out var stationAiCustomization);
+
+ // Create UI entires for each group of customizations
+ var groupPrototypes = _protoManager.EnumeratePrototypes<StationAiCustomizationGroupPrototype>();
+ groupPrototypes = groupPrototypes.OrderBy(x => x.ID); // To ensure consistency in presentation
+
+ foreach (var groupPrototype in groupPrototypes)
+ {
+ StationAiCustomizationPrototype? selectedPrototype = null;
+
+ if (stationAiCustomization?.ProtoIds.TryGetValue(groupPrototype, out var selectedProtoId) == true)
+ _protoManager.TryIndex(selectedProtoId, out selectedPrototype);
+
+ _buttonGroups[groupPrototype] = new ButtonGroup();
+ _groupContainers[groupPrototype] = new StationAiCustomizationGroupContainer(groupPrototype, selectedPrototype, _buttonGroups[groupPrototype], this, _protoManager);
+ CustomizationGroupsContainer.AddTab(_groupContainers[groupPrototype], Loc.GetString(groupPrototype.Name));
+ }
+
+ Title = Loc.GetString("station-ai-customization-menu");
+ }
+
+ public void OnSendStationAiCustomizationMessage
+ (ProtoId<StationAiCustomizationGroupPrototype> groupProtoId, ProtoId<StationAiCustomizationPrototype> customizationProtoId)
+ {
+ SendStationAiCustomizationMessageAction?.Invoke(groupProtoId, customizationProtoId);
+ }
+
+ private sealed class StationAiCustomizationGroupContainer : BoxContainer
+ {
+ public StationAiCustomizationGroupContainer
+ (StationAiCustomizationGroupPrototype groupPrototype,
+ StationAiCustomizationPrototype? selectedPrototype,
+ ButtonGroup buttonGroup,
+ StationAiCustomizationMenu menu,
+ IPrototypeManager protoManager)
+ {
+ Orientation = LayoutOrientation.Vertical;
+ HorizontalExpand = true;
+ VerticalExpand = true;
+
+ // Create UI entries for all customization in the group
+ foreach (var protoId in groupPrototype.ProtoIds)
+ {
+ if (!protoManager.TryIndex(protoId, out var prototype))
+ continue;
+
+ var entry = new StationAiCustomizationEntryContainer(groupPrototype, prototype, buttonGroup, menu);
+ AddChild(entry);
+
+ if (prototype == selectedPrototype)
+ entry.SelectButton.Pressed = true;
+ }
+ }
+ }
+
+ private sealed class StationAiCustomizationEntryContainer : BoxContainer
+ {
+ public ProtoId<StationAiCustomizationPrototype> ProtoId;
+ public Button SelectButton;
+
+ public StationAiCustomizationEntryContainer
+ (StationAiCustomizationGroupPrototype groupPrototype,
+ StationAiCustomizationPrototype prototype,
+ ButtonGroup buttonGroup,
+ StationAiCustomizationMenu menu)
+ {
+ ProtoId = prototype;
+
+ Orientation = LayoutOrientation.Horizontal;
+ HorizontalExpand = true;
+
+ // Create a selection button
+ SelectButton = new Button
+ {
+ Text = Loc.GetString(prototype.Name),
+ HorizontalExpand = true,
+ ToggleMode = true,
+ Group = buttonGroup,
+ };
+
+ SelectButton.OnPressed += args =>
+ {
+ menu.OnSendStationAiCustomizationMessage(groupPrototype, prototype);
+ };
+
+ AddChild(SelectButton);
+
+ // Creat a background for the preview
+ var background = new AnimatedTextureRect
+ {
+ HorizontalAlignment = HAlignment.Center,
+ VerticalAlignment = VAlignment.Center,
+ SetWidth = 56,
+ SetHeight = 56,
+ Margin = new Thickness(10f, 2f),
+ };
+
+ background.DisplayRect.TextureScale = new Vector2(IconScale, IconScale);
+
+ if (prototype.PreviewBackground != null)
+ {
+ background.SetFromSpriteSpecifier(prototype.PreviewBackground);
+ }
+
+ AddChild(background);
+
+ // Create a preview icon
+ var icon = new AnimatedTextureRect
+ {
+ HorizontalAlignment = HAlignment.Center,
+ VerticalAlignment = VAlignment.Center,
+ SetWidth = 56,
+ SetHeight = 56,
+ };
+
+ icon.DisplayRect.TextureScale = new Vector2(IconScale, IconScale);
+
+ // Default RSI path/state
+ var rsiPath = prototype.LayerData.FirstOrNull()?.Value.RsiPath;
+ var rsiState = prototype.LayerData.FirstOrNull()?.Value.State;
+
+ // Specified RSI path/state
+ if (!string.IsNullOrEmpty(prototype.PreviewKey) && prototype.LayerData.TryGetValue(prototype.PreviewKey, out var layerData))
+ {
+ rsiPath = layerData.RsiPath;
+ rsiState = layerData.State;
+ }
+
+ // Update icon
+ if (rsiPath != null && rsiState != null)
+ {
+ var specifier = new SpriteSpecifier.Rsi(new ResPath(rsiPath), rsiState);
+ icon.SetFromSpriteSpecifier(specifier);
+ }
+
+ background.AddChild(icon);
+ }
+ }
+}
+
+
using Content.Shared.Silicons.StationAi;
+using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Player;
{
[Dependency] private readonly IOverlayManager _overlayMgr = default!;
[Dependency] private readonly IPlayerManager _player = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
private StationAiOverlay? _overlay;
SubscribeLocalEvent<StationAiOverlayComponent, LocalPlayerDetachedEvent>(OnAiDetached);
SubscribeLocalEvent<StationAiOverlayComponent, ComponentInit>(OnAiOverlayInit);
SubscribeLocalEvent<StationAiOverlayComponent, ComponentRemove>(OnAiOverlayRemove);
+ SubscribeLocalEvent<StationAiCoreComponent, AppearanceChangeEvent>(OnAppearanceChange);
}
private void OnAiOverlayInit(Entity<StationAiOverlayComponent> ent, ref ComponentInit args)
RemoveOverlay();
}
+ private void OnAppearanceChange(Entity<StationAiCoreComponent> entity, ref AppearanceChangeEvent args)
+ {
+ if (args.Sprite == null)
+ return;
+
+ if (_appearance.TryGetData<PrototypeLayerData>(entity.Owner, StationAiVisualState.Key, out var layerData, args.Component))
+ args.Sprite.LayerSetData(StationAiVisualState.Key, layerData);
+
+ args.Sprite.LayerSetVisible(StationAiVisualState.Key, layerData != null);
+ }
+
public override void Shutdown()
{
base.Shutdown();
/// The prototype sprite layer data for the hologram
/// </summary>
[DataField, AutoNetworkedField]
- public PrototypeLayerData[] LayerData;
+ public PrototypeLayerData[]? LayerData = null;
}
--- /dev/null
+using Content.Shared.Holopad;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Silicons.StationAi;
+
+public abstract partial class SharedStationAiSystem
+{
+ private ProtoId<StationAiCustomizationGroupPrototype> _stationAiCoreCustomGroupProtoId = "StationAiCoreIconography";
+ private ProtoId<StationAiCustomizationGroupPrototype> _stationAiHologramCustomGroupProtoId = "StationAiHolograms";
+
+ private void InitializeCustomization()
+ {
+ SubscribeLocalEvent<StationAiCoreComponent, StationAiCustomizationMessage>(OnStationAiCustomization);
+ }
+
+ private void OnStationAiCustomization(Entity<StationAiCoreComponent> entity, ref StationAiCustomizationMessage args)
+ {
+ if (!_protoManager.TryIndex(args.GroupProtoId, out var groupPrototype) || !_protoManager.TryIndex(args.CustomizationProtoId, out var customizationProto))
+ return;
+
+ if (!TryGetHeld((entity, entity.Comp), out var held))
+ return;
+
+ if (!TryComp<StationAiCustomizationComponent>(held, out var stationAiCustomization))
+ return;
+
+ if (stationAiCustomization.ProtoIds.TryGetValue(args.GroupProtoId, out var protoId) && protoId == args.CustomizationProtoId)
+ return;
+
+ stationAiCustomization.ProtoIds[args.GroupProtoId] = args.CustomizationProtoId;
+
+ Dirty(held, stationAiCustomization);
+
+ // Update hologram
+ if (groupPrototype.Category == StationAiCustomizationType.Hologram)
+ UpdateHolographicAvatar((held, stationAiCustomization));
+
+ // Update core iconography
+ if (groupPrototype.Category == StationAiCustomizationType.CoreIconography && TryComp<StationAiHolderComponent>(entity, out var stationAiHolder))
+ UpdateAppearance((entity, stationAiHolder));
+ }
+
+ private void UpdateHolographicAvatar(Entity<StationAiCustomizationComponent> entity)
+ {
+ if (!TryComp<HolographicAvatarComponent>(entity, out var avatar))
+ return;
+
+ if (!entity.Comp.ProtoIds.TryGetValue(_stationAiHologramCustomGroupProtoId, out var protoId))
+ return;
+
+ if (!_protoManager.TryIndex(protoId, out var prototype))
+ return;
+
+ if (!prototype.LayerData.TryGetValue(StationAiState.Hologram.ToString(), out var layerData))
+ return;
+
+ avatar.LayerData = [layerData];
+ Dirty(entity, avatar);
+ }
+
+ private void CustomizeAppearance(Entity<StationAiCoreComponent> entity, StationAiState state)
+ {
+ var stationAi = GetInsertedAI(entity);
+
+ if (stationAi == null)
+ {
+ _appearance.RemoveData(entity.Owner, StationAiVisualState.Key);
+ return;
+ }
+
+ if (!TryComp<StationAiCustomizationComponent>(stationAi, out var stationAiCustomization) ||
+ !stationAiCustomization.ProtoIds.TryGetValue(_stationAiCoreCustomGroupProtoId, out var protoId) ||
+ !_protoManager.TryIndex(protoId, out var prototype) ||
+ !prototype.LayerData.TryGetValue(state.ToString(), out var layerData))
+ {
+ return;
+ }
+
+ // This data is handled manually in the client StationAiSystem
+ _appearance.SetData(entity.Owner, StationAiVisualState.Key, layerData);
+ }
+}
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using System.Diagnostics.CodeAnalysis;
+using Robust.Shared.Utility;
namespace Content.Shared.Silicons.StationAi;
[Dependency] private readonly SharedTransformSystem _xforms = default!;
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly StationAiVisionSystem _vision = default!;
+ [Dependency] private readonly IPrototypeManager _protoManager = default!;
// StationAiHeld is added to anything inside of an AI core.
// StationAiHolder indicates it can hold an AI positronic brain (e.g. holocard / core).
InitializeAirlock();
InitializeHeld();
InitializeLight();
+ InitializeCustomization();
SubscribeLocalEvent<StationAiWhitelistComponent, BoundUserInterfaceCheckRangeEvent>(OnAiBuiCheck);
private void OnCoreVerbs(Entity<StationAiCoreComponent> ent, ref GetVerbsEvent<Verb> args)
{
- if (!_admin.IsAdmin(args.User) ||
- TryGetHeld((ent.Owner, ent.Comp), out _))
+ var user = args.User;
+
+ // Admin option to take over the station AI core
+ if (_admin.IsAdmin(args.User) &&
+ !TryGetHeld((ent.Owner, ent.Comp), out _))
{
- return;
+ args.Verbs.Add(new Verb()
+ {
+ Text = Loc.GetString("station-ai-takeover"),
+ Category = VerbCategory.Debug,
+ Act = () =>
+ {
+ var brain = SpawnInContainerOrDrop(DefaultAi, ent.Owner, StationAiCoreComponent.Container);
+ _mind.ControlMob(user, brain);
+ },
+ Impact = LogImpact.High,
+ });
}
- var user = args.User;
-
- args.Verbs.Add(new Verb()
+ // Option to open the station AI customization menu
+ if (TryGetHeld((ent, ent.Comp), out var insertedAi) && insertedAi == user)
{
- Text = Loc.GetString("station-ai-takeover"),
- Category = VerbCategory.Debug,
- Act = () =>
+ args.Verbs.Add(new Verb()
{
- var brain = SpawnInContainerOrDrop(DefaultAi, ent.Owner, StationAiCoreComponent.Container);
- _mind.ControlMob(user, brain);
- },
- Impact = LogImpact.High,
- });
+ Text = Loc.GetString("station-ai-customization-menu"),
+ Act = () => _uiSystem.TryOpenUi(ent.Owner, StationAiCustomizationUiKey.Key, insertedAi),
+ Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/emotes.svg.192dpi.png")),
+ });
+ }
}
private void OnAiAccessible(Entity<StationAiOverlayComponent> ent, ref AccessibleOverrideEvent args)
if (!Resolve(entity.Owner, ref entity.Comp, false))
return;
- if (!_containers.TryGetContainer(entity.Owner, StationAiHolderComponent.Container, out var container) ||
- container.Count == 0)
+ // Todo: when AIs can die, add a check to see if the AI is in the 'dead' state
+ var state = StationAiState.Empty;
+
+ if (_containers.TryGetContainer(entity.Owner, StationAiHolderComponent.Container, out var container) && container.Count > 0)
+ state = StationAiState.Occupied;
+
+ // If the entity is a station AI core, attempt to customize its appearance
+ if (TryComp<StationAiCoreComponent>(entity, out var stationAiCore))
{
- _appearance.SetData(entity.Owner, StationAiVisualState.Key, StationAiState.Empty);
+ CustomizeAppearance((entity, stationAiCore), state);
return;
}
- _appearance.SetData(entity.Owner, StationAiVisualState.Key, StationAiState.Occupied);
+ // Otherwise let generic visualizers handle the appearance update
+ _appearance.SetData(entity.Owner, StationAiVisualState.Key, state);
}
public virtual void AnnounceIntellicardUsage(EntityUid uid, SoundSpecifier? cue = null) { }
[Serializable, NetSerializable]
public sealed partial class IntellicardDoAfterEvent : SimpleDoAfterEvent;
-
[Serializable, NetSerializable]
public enum StationAiVisualState : byte
{
Key,
}
+[Serializable, NetSerializable]
+public enum StationAiSpriteState : byte
+{
+ Key,
+}
+
[Serializable, NetSerializable]
public enum StationAiState : byte
{
Empty,
Occupied,
Dead,
+ Hologram,
}
--- /dev/null
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Silicons.StationAi;
+
+/// <summary>
+/// Holds data for altering the appearance of station AIs.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class StationAiCustomizationComponent : Component
+{
+ /// <summary>
+ /// Dictionary of the prototype data used for customizing the appearance of the entity.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public Dictionary<ProtoId<StationAiCustomizationGroupPrototype>, ProtoId<StationAiCustomizationPrototype>> ProtoIds = new();
+}
+
+/// <summary>
+/// Message sent to server that contains a station AI customization that the client has selected
+/// </summary>
+[Serializable, NetSerializable]
+public sealed class StationAiCustomizationMessage : BoundUserInterfaceMessage
+{
+ public readonly ProtoId<StationAiCustomizationGroupPrototype> GroupProtoId;
+ public readonly ProtoId<StationAiCustomizationPrototype> CustomizationProtoId;
+
+ public StationAiCustomizationMessage(ProtoId<StationAiCustomizationGroupPrototype> groupProtoId, ProtoId<StationAiCustomizationPrototype> customizationProtoId)
+ {
+ GroupProtoId = groupProtoId;
+ CustomizationProtoId = customizationProtoId;
+ }
+}
+
+/// <summary>
+/// Key for opening the station AI customization UI
+/// </summary>
+[Serializable, NetSerializable]
+public enum StationAiCustomizationUiKey : byte
+{
+ Key,
+}
+
+/// <summary>
+/// The different catagories of station Ai customizations available
+/// </summary>
+[Serializable, NetSerializable]
+public enum StationAiCustomizationType : byte
+{
+ CoreIconography,
+ Hologram,
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Silicons.StationAi;
+
+/// <summary>
+/// Holds data for customizing the appearance of station AIs.
+/// </summary>
+[Prototype]
+public sealed partial class StationAiCustomizationGroupPrototype : IPrototype
+{
+ [IdDataField]
+ public string ID { get; } = string.Empty;
+
+ /// <summary>
+ /// The localized name of the customization.
+ /// </summary>
+ [DataField(required: true)]
+ public LocId Name;
+
+ /// <summary>
+ /// The type of customization that is associated with this group.
+ /// </summary>
+ [DataField]
+ public StationAiCustomizationType Category = StationAiCustomizationType.CoreIconography;
+
+ /// <summary>
+ /// The list of prototypes associated with the customization group.
+ /// </summary>
+ [DataField(required: true)]
+ public List<ProtoId<StationAiCustomizationPrototype>> ProtoIds = new();
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Silicons.StationAi;
+
+/// <summary>
+/// Holds data for customizing the appearance of station AIs.
+/// </summary>
+[Prototype]
+public sealed partial class StationAiCustomizationPrototype : IPrototype, IInheritingPrototype
+{
+ [IdDataField]
+ public string ID { get; } = string.Empty;
+
+ /// <summary>
+ /// The (unlocalized) name of the customization.
+ /// </summary>
+ [DataField(required: true)]
+ public LocId Name;
+
+ /// <summary>
+ /// Stores the data which is used to modify the appearance of the station AI.
+ /// </summary>
+ [DataField(required: true)]
+ public Dictionary<string, PrototypeLayerData> LayerData = new();
+
+ /// <summary>
+ /// Key used to index the prototype layer data and extract a preview of the customization (for menus, etc)
+ /// </summary>
+ [DataField]
+ public string PreviewKey = string.Empty;
+
+ /// <summary>
+ /// Specifies a background to use for previewing the customization (for menus, etc)
+ /// </summary>
+ [DataField]
+ public SpriteSpecifier? PreviewBackground;
+
+ /// <summary>
+ /// The prototype we inherit from.
+ /// </summary>
+ [ViewVariables]
+ [ParentDataFieldAttribute(typeof(AbstractPrototypeIdArraySerializer<StationAiCustomizationPrototype>))]
+ public string[]? Parents { get; }
+
+ /// <summary>
+ /// Specifies whether the prototype is abstract.
+ /// </summary>
+ [ViewVariables]
+ [NeverPushInheritance]
+ [AbstractDataField]
+ public bool Abstract { get; }
+}
ai-device-not-responding = Device is not responding
ai-consciousness-download-warning = Your consciousness is being downloaded.
+
+# UI
+station-ai-customization-menu = AI customization
+station-ai-customization-categories = Categories
+station-ai-customization-options = Options (choice of one)
+station-ai-customization-core = AI core displays
+station-ai-customization-hologram = Holographic avatars
+
+# Customizations
+station-ai-icon-ai = Ghost in the machine
+station-ai-icon-angel = Guardian angel
+station-ai-icon-bliss = Simpler times
+station-ai-icon-clown = Clownin' around
+station-ai-icon-dorf = Adventure awaits
+station-ai-icon-heartline = Lifeline
+station-ai-icon-smiley = All smiles
+
+station-ai-hologram-female = Female appearance
+station-ai-hologram-male = Male appearance
+station-ai-hologram-face = Disembodied head
+station-ai-hologram-cat = Cat form
+station-ai-hologram-dog = Corgi form
\ No newline at end of file
--- /dev/null
+# Groups
+- type: stationAiCustomizationGroup
+ id: StationAiCoreIconography
+ name: station-ai-customization-core
+ category: CoreIconography
+ protoIds:
+ - StationAiIconAi
+ - StationAiIconAngel
+ - StationAiIconBliss
+ - StationAiIconClown
+ - StationAiIconDorf
+ - StationAiIconHeartline
+ - StationAiIconSmiley
+
+- type: stationAiCustomizationGroup
+ id: StationAiHolograms
+ name: station-ai-customization-hologram
+ category: Hologram
+ protoIds:
+ - StationAiHologramFemale
+ - StationAiHologramMale
+ - StationAiHologramFace
+ - StationAiHologramCat
+ - StationAiHologramDog
+
+# Iconography
+- type: stationAiCustomization
+ abstract: true
+ id: StationAiIconBase
+ previewKey: Occupied
+ previewBackground:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: base
+
+- type: stationAiCustomization
+ parent: StationAiIconBase
+ id: StationAiIconAi
+ name: station-ai-icon-ai
+ layerData:
+ Occupied:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: ai
+ Dead:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: ai_dead
+
+- type: stationAiCustomization
+ parent: StationAiIconBase
+ id: StationAiIconAngel
+ name: station-ai-icon-angel
+ layerData:
+ Occupied:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: ai_angel
+ Dead:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: ai_angel_dead
+
+- type: stationAiCustomization
+ parent: StationAiIconBase
+ id: StationAiIconBliss
+ name: station-ai-icon-bliss
+ layerData:
+ Occupied:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: ai_bliss
+ Dead:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: ai_dead
+
+- type: stationAiCustomization
+ parent: StationAiIconBase
+ id: StationAiIconClown
+ name: station-ai-icon-clown
+ layerData:
+ Occupied:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: ai_clown
+ Dead:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: ai_clown_dead
+
+- type: stationAiCustomization
+ parent: StationAiIconBase
+ id: StationAiIconDorf
+ name: station-ai-icon-dorf
+ layerData:
+ Occupied:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: ai_dorf
+ Dead:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: ai_dead
+
+- type: stationAiCustomization
+ parent: StationAiIconBase
+ id: StationAiIconHeartline
+ name: station-ai-icon-heartline
+ layerData:
+ Occupied:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: "ai_heartline"
+ Dead:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: "ai_heartline_dead"
+
+- type: stationAiCustomization
+ parent: StationAiIconBase
+ id: StationAiIconSmiley
+ name: station-ai-icon-smiley
+ layerData:
+ Occupied:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: "ai_smiley"
+ Dead:
+ sprite: Mobs/Silicon/station_ai.rsi
+ state: "ai_dead"
+
+# Holograms
+- type: stationAiCustomization
+ id: StationAiHologramFemale
+ name: station-ai-hologram-female
+ previewKey: Hologram
+ layerData:
+ Hologram:
+ sprite: Mobs/Silicon/holograms.rsi
+ state: ai_female
+
+- type: stationAiCustomization
+ id: StationAiHologramMale
+ name: station-ai-hologram-male
+ previewKey: Hologram
+ layerData:
+ Hologram:
+ sprite: Mobs/Silicon/holograms.rsi
+ state: ai_male
+
+- type: stationAiCustomization
+ id: StationAiHologramFace
+ name: station-ai-hologram-face
+ previewKey: Hologram
+ layerData:
+ Hologram:
+ sprite: Mobs/Silicon/holograms.rsi
+ state: ai_face
+
+- type: stationAiCustomization
+ id: StationAiHologramCat
+ name: station-ai-hologram-cat
+ previewKey: Hologram
+ layerData:
+ Hologram:
+ sprite: Mobs/Silicon/holograms.rsi
+ state: ai_cat
+
+- type: stationAiCustomization
+ id: StationAiHologramDog
+ name: station-ai-hologram-dog
+ previewKey: Hologram
+ layerData:
+ Hologram:
+ sprite: Mobs/Silicon/holograms.rsi
+ state: ai_dog
canShuttle: false
title: comms-console-announcement-title-station-ai
color: "#5ed7aa"
- - type: HolographicAvatar
- layerData:
- - sprite: Mobs/Silicon/station_ai.rsi
- state: default
- type: ShowJobIcons
- type: entity
layers:
- state: base
- state: ai_empty
- map: ["unshaded"]
shader: unshaded
+ - state: ai
+ map: ["enum.StationAiVisualState.Key"]
+ shader: unshaded
+ visible: false
- type: Appearance
- type: InteractionPopup
interactSuccessString: petting-success-station-ai
messagePerceivedByOthers: petting-success-station-ai-others # Otherwise AI cannot tell its being pet as It's just a brain inside of the core, not the core itself.
interactSuccessSound:
path: /Audio/Ambience/Objects/periodic_beep.ogg
- - type: GenericVisualizer
- visuals:
- enum.StationAiVisualState.Key:
- unshaded:
- Empty: { state: ai_empty }
- Occupied: { state: ai }
- type: Telephone
compatibleRanges:
- Grid
- type: StationAiWhitelist
- type: UserInterface
interfaces:
- enum.HolopadUiKey.AiRequestWindow:
- type: HolopadBoundUserInterface
- enum.HolopadUiKey.AiActionWindow:
- type: HolopadBoundUserInterface
+ enum.HolopadUiKey.AiRequestWindow:
+ type: HolopadBoundUserInterface
+ enum.HolopadUiKey.AiActionWindow:
+ type: HolopadBoundUserInterface
+ enum.StationAiCustomizationUiKey.Key:
+ type: StationAiCustomizationBoundUserInterface
# The job-ready version of an AI spawn.
- type: entity
- type: ContainerSpawnPoint
containerId: station_ai_mind_slot
job: StationAi
- - type: Sprite
- sprite: Mobs/Silicon/station_ai.rsi
- layers:
- - state: base
- - state: ai
- shader: unshaded
# The actual brain inside the core
- type: entity
- type: StartingMindRole
mindRole: "MindRoleSiliconBrain"
silent: true
+ - type: StationAiCustomization
+ protoIds:
+ StationAiCoreIconography: StationAiIconAi
+ StationAiHolograms: StationAiHologramFemale
+ - type: HolographicAvatar
+ layerData:
+ - sprite: Mobs/Silicon/holograms.rsi
+ state: ai_female
- type: NameIdentifier
group: StationAi
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from vgstation at https://github.com/vgstation-coders/vgstation13/blob/e2923df112df8aa025846d0764697bad6506586a/icons/mob/AI.dmi - modified by chromiumboy.",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "ai_female"
+ },
+ {
+ "name": "ai_male"
+ },
+ {
+ "name": "ai_face",
+ "delays": [
+ [
+ 2.3,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "ai_cat",
+ "delays": [
+ [
+ 0.75,
+ 0.75
+ ]
+ ]
+ },
+ {
+ "name": "ai_dog"
+ }
+ ]
+}
{
- "version": 1,
- "license": "CC-BY-SA-3.0",
- "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/2a19963297f91efb452dbb5c1d4eb28a14776b0a/icons/mob/silicon/ai.dmi",
- "size": {
- "x": 32,
- "y": 32
- },
- "states": [
- {
- "name": "ai",
- "delays": [
- [
- 0.2,
- 0.2,
- 0.1,
- 0.2,
- 0.2,
- 0.2,
- 0.2,
- 0.2,
- 0.2,
- 0.2,
- 0.2,
- 0.2,
- 0.2,
- 0.2,
- 0.1
- ]
- ]
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from vgstation at https://github.com/vgstation-coders/vgstation13/blob/e2923df112df8aa025846d0764697bad6506586a/icons/mob/AI.dmi - modified by chromiumboy.",
+ "size": {
+ "x": 32,
+ "y": 32
},
- {
- "name": "ai_camera",
- "delays": [
- [
- 1.0,
- 1.0
- ]
- ]
- },
- {
- "name": "ai_dead"
- },
- {
- "name": "ai_empty",
- "delays": [
- [
- 0.7,
- 0.7
- ]
- ]
- },
- {
- "name": "default",
- "directions": 4
- },
- {
- "name": "base"
- }
- ]
-}
\ No newline at end of file
+ "states": [
+ {
+ "name": "ai",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.1,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "ai_angel",
+ "delays": [
+ [
+ 0.08,
+ 0.08,
+ 0.08,
+ 0.08,
+ 0.08,
+ 0.08
+ ]
+ ]
+ },
+ {
+ "name": "ai_angel_dead",
+ "delays": [
+ [
+ 1.00,
+ 0.08,
+ 0.50,
+ 0.20
+ ]
+ ]
+ },
+ {
+ "name": "ai_bliss"
+ },
+ {
+ "name": "ai_clown",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "ai_clown_dead",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "ai_dorf",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "ai_heartline",
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "ai_heartline_dead",
+ "delays": [
+ [
+ 0.15,
+ 0.15,
+ 0.15,
+ 0.15,
+ 0.15,
+ 0.15,
+ 0.15,
+ 0.15,
+ 0.15,
+ 0.15,
+ 0.15,
+ 0.15,
+ 0.15
+ ]
+ ]
+ },
+ {
+ "name": "ai_smiley",
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "ai_camera",
+ "delays": [
+ [
+ 1.0,
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "ai_dead"
+ },
+ {
+ "name": "ai_empty",
+ "delays": [
+ [
+ 0.7,
+ 0.7
+ ]
+ ]
+ },
+ {
+ "name": "default",
+ "directions": 4
+ },
+ {
+ "name": "base"
+ }
+ ]
+}