Station AI customizations (#34501)

Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
This commit is contained in:
chromiumboy
2025-05-13 03:50:43 -05:00
committed by GitHub
parent 0eeb46cf9e
commit 7ac67ebf8c
29 changed files with 974 additions and 101 deletions

View File

@@ -0,0 +1,40 @@
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();
}
}

View File

@@ -0,0 +1,23 @@
<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>

View File

@@ -0,0 +1,176 @@
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);
}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.Silicons.StationAi;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Player;
@@ -9,6 +10,7 @@ public sealed partial class StationAiSystem : SharedStationAiSystem
{
[Dependency] private readonly IOverlayManager _overlayMgr = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
private StationAiOverlay? _overlay;
@@ -22,6 +24,7 @@ public sealed partial class StationAiSystem : SharedStationAiSystem
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)
@@ -72,6 +75,17 @@ public sealed partial class StationAiSystem : SharedStationAiSystem
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();

View File

@@ -9,5 +9,5 @@ public sealed partial class HolographicAvatarComponent : Component
/// The prototype sprite layer data for the hologram
/// </summary>
[DataField, AutoNetworkedField]
public PrototypeLayerData[] LayerData;
public PrototypeLayerData[]? LayerData = null;
}

View File

@@ -0,0 +1,82 @@
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);
}
}

View File

@@ -28,6 +28,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using System.Diagnostics.CodeAnalysis;
using Robust.Shared.Utility;
namespace Content.Shared.Silicons.StationAi;
@@ -56,6 +57,7 @@ public abstract partial class SharedStationAiSystem : EntitySystem
[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).
@@ -82,6 +84,7 @@ public abstract partial class SharedStationAiSystem : EntitySystem
InitializeAirlock();
InitializeHeld();
InitializeLight();
InitializeCustomization();
SubscribeLocalEvent<StationAiWhitelistComponent, BoundUserInterfaceCheckRangeEvent>(OnAiBuiCheck);
@@ -107,14 +110,12 @@ public abstract partial class SharedStationAiSystem : EntitySystem
private void OnCoreVerbs(Entity<StationAiCoreComponent> ent, ref GetVerbsEvent<Verb> args)
{
if (!_admin.IsAdmin(args.User) ||
TryGetHeld((ent.Owner, ent.Comp), out _))
{
return;
}
var user = args.User;
// Admin option to take over the station AI core
if (_admin.IsAdmin(args.User) &&
!TryGetHeld((ent.Owner, ent.Comp), out _))
{
args.Verbs.Add(new Verb()
{
Text = Loc.GetString("station-ai-takeover"),
@@ -128,6 +129,18 @@ public abstract partial class SharedStationAiSystem : EntitySystem
});
}
// Option to open the station AI customization menu
if (TryGetHeld((ent, ent.Comp), out var insertedAi) && insertedAi == user)
{
args.Verbs.Add(new Verb()
{
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)
{
args.Handled = true;
@@ -494,14 +507,21 @@ public abstract partial class SharedStationAiSystem : EntitySystem
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) { }
@@ -550,17 +570,23 @@ public sealed partial class JumpToCoreEvent : InstantActionEvent
[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,
}

View File

@@ -0,0 +1,53 @@
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,
}

View File

@@ -0,0 +1,31 @@
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();
}

View File

@@ -0,0 +1,54 @@
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; }
}

View File

@@ -22,3 +22,25 @@ toggle-light = Toggle light
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

View File

@@ -0,0 +1,163 @@
# 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

View File

@@ -70,10 +70,6 @@
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
@@ -202,8 +198,11 @@
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
@@ -211,12 +210,6 @@
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
@@ -234,6 +227,8 @@
type: HolopadBoundUserInterface
enum.HolopadUiKey.AiActionWindow:
type: HolopadBoundUserInterface
enum.StationAiCustomizationUiKey.Key:
type: StationAiCustomizationBoundUserInterface
# The job-ready version of an AI spawn.
- type: entity
@@ -244,12 +239,6 @@
- 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
@@ -294,6 +283,14 @@
- 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,38 @@
{
"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"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -1,7 +1,7 @@
{
"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",
"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
@@ -29,6 +29,160 @@
]
]
},
{
"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": [