Skip to content
This repository was archived by the owner on Apr 21, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
24 changes: 19 additions & 5 deletions Content.Client/Clothing/ClientClothingSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public override void Initialize()
base.Initialize();

SubscribeLocalEvent<ClothingComponent, GetEquipmentVisualsEvent>(OnGetVisuals);
SubscribeLocalEvent<ClothingComponent, InventoryTemplateUpdated>(OnInventoryTemplateUpdated);

SubscribeLocalEvent<InventoryComponent, VisualsChangedEvent>(OnVisualsChanged);
SubscribeLocalEvent<SpriteComponent, DidUnequipEvent>(OnDidUnequip);
Expand All @@ -74,11 +75,7 @@ private void OnAppearanceUpdate(EntityUid uid, InventoryComponent component, ref
if (args.Sprite == null)
return;

var enumerator = _inventorySystem.GetSlotEnumerator((uid, component));
while (enumerator.NextItem(out var item, out var slot))
{
RenderEquipment(uid, item, slot.Name, component);
}
UpdateAllSlots(uid, component);

// No clothing equipped -> make sure the layer is hidden, though this should already be handled by on-unequip.
if (_sprite.LayerMapTryGet((uid, args.Sprite), HumanoidVisualLayers.StencilMask, out var layer, false))
Expand All @@ -88,6 +85,23 @@ private void OnAppearanceUpdate(EntityUid uid, InventoryComponent component, ref
}
}

private void OnInventoryTemplateUpdated(Entity<ClothingComponent> ent, ref InventoryTemplateUpdated args)
{
UpdateAllSlots(ent.Owner, clothing: ent.Comp);
}

private void UpdateAllSlots(
EntityUid uid,
InventoryComponent? inventoryComponent = null,
ClothingComponent? clothing = null)
{
var enumerator = _inventorySystem.GetSlotEnumerator((uid, inventoryComponent));
while (enumerator.NextItem(out var item, out var slot))
{
RenderEquipment(uid, item, slot.Name, inventoryComponent, clothingComponent: clothing);
}
}

private void OnGetVisuals(EntityUid uid, ClothingComponent item, GetEquipmentVisualsEvent args)
{
if (!TryComp(args.Equipee, out InventoryComponent? inventory))
Expand Down
16 changes: 15 additions & 1 deletion Content.Client/Inventory/ClientInventorySystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,23 @@ public void UIInventoryAltActivateItem(string slot, EntityUid uid)
EntityManager.RaisePredictiveEvent(new InteractInventorySlotEvent(GetNetEntity(item.Value), altInteract: true));
}

protected override void UpdateInventoryTemplate(Entity<InventoryComponent> ent)
{
base.UpdateInventoryTemplate(ent);

if (TryComp(ent, out InventorySlotsComponent? inventorySlots))
{
foreach (var slot in ent.Comp.Slots)
{
if (inventorySlots.SlotData.TryGetValue(slot.Name, out var slotData))
slotData.SlotDef = slot;
}
}
}

public sealed class SlotData
{
public readonly SlotDefinition SlotDef;
public SlotDefinition SlotDef;
public EntityUid? HeldEntity => Container?.ContainedEntity;
public bool Blocked;
public bool Highlighted;
Expand Down
1 change: 1 addition & 0 deletions Content.Client/Overlays/EquipmentHudSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
{
[Dependency] private readonly IPlayerManager _player = default!;

[ViewVariables]
protected bool IsActive;
protected virtual SlotFlags TargetSlots => ~SlotFlags.POCKET;

Expand Down
3 changes: 2 additions & 1 deletion Content.Client/Silicons/Borgs/BorgMenu.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ private void UpdateModulePanel()
_modules.Clear();
foreach (var module in chassis.ModuleContainer.ContainedEntities)
{
var control = new BorgModuleControl(module, _entity);
var moduleComponent = _entity.GetComponent<BorgModuleComponent>(module);
var control = new BorgModuleControl(module, _entity, !moduleComponent.DefaultModule);
control.RemoveButtonPressed += () =>
{
RemoveModuleButtonPressed?.Invoke(module);
Expand Down
3 changes: 2 additions & 1 deletion Content.Client/Silicons/Borgs/BorgModuleControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public sealed partial class BorgModuleControl : PanelContainer
{
public Action? RemoveButtonPressed;

public BorgModuleControl(EntityUid entity, IEntityManager entityManager)
public BorgModuleControl(EntityUid entity, IEntityManager entityManager, bool canRemove)
{
RobustXamlLoader.Load(this);

Expand All @@ -20,6 +20,7 @@ public BorgModuleControl(EntityUid entity, IEntityManager entityManager)
{
RemoveButtonPressed?.Invoke();
};
RemoveButton.Visible = canRemove;
}
}

47 changes: 47 additions & 0 deletions Content.Client/Silicons/Borgs/BorgSelectTypeMenu.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:chassisSpriteControls="clr-namespace:Content.Client._CD.Silicons.Borgs.UI"
Title="{Loc 'borg-select-type-menu-title'}"
SetSize="600 480">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
<!-- Left pane: selection of borg type -->
<BoxContainer Orientation="Vertical" MinWidth="200" Margin="2 0">
<Label Text="{Loc 'borg-select-type-menu-available'}" StyleClasses="LabelHeading" />
<ScrollContainer HScrollEnabled="False" VerticalExpand="True">
<BoxContainer Name="SelectionsContainer" Orientation="Vertical" />
</ScrollContainer>
</BoxContainer>

<customControls:VSeparator />

<!-- Right pane: information about selected borg module, confirm button. -->
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="2 0">
<Label Text="{Loc 'borg-select-type-menu-information'}" StyleClasses="LabelHeading" />
<Control VerticalExpand="True">
<controls:Placeholder Name="InfoPlaceholder" PlaceholderText="{Loc 'borg-select-type-menu-select-type'}" />
<BoxContainer Name="InfoContents" Orientation="Vertical" Visible="False">
<BoxContainer Orientation="Horizontal" Margin="0 0 0 4">
<EntityPrototypeView Name="ChassisView" Scale="2,2" />
<Label Name="NameLabel" HorizontalExpand="True" />
</BoxContainer>

<RichTextLabel Name="DescriptionLabel" VerticalExpand="True" VerticalAlignment="Top" />
</BoxContainer>
</Control>

<chassisSpriteControls:ChassisSpriteSelection Name="ChassisSpriteSelection"/><!-- CD change -->

<controls:ConfirmButton Name="ConfirmTypeButton" Text="{Loc 'borg-select-type-menu-confirm'}"
Disabled="True" HorizontalAlignment="Right"
MinWidth="200" />
</BoxContainer>
</BoxContainer>

<controls:StripeBack Margin="0 0 0 4">
<Label Text="{Loc 'borg-select-type-menu-bottom-text'}" HorizontalAlignment="Center" StyleClasses="LabelSubText" Margin="4 4 0 4"/>
</controls:StripeBack>
</BoxContainer>

</controls:FancyWindow>
98 changes: 98 additions & 0 deletions Content.Client/Silicons/Borgs/BorgSelectTypeMenu.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System.Linq;
using Content.Client._CD.Silicons.Borgs.UI;
using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Systems.Guidebook;
using Content.Shared._CD.Silicons;
using Content.Shared._CD.Silicons.Borgs;
using Content.Shared.Guidebook;
using Content.Shared.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;

namespace Content.Client.Silicons.Borgs;

/// <summary>
/// Menu used by borgs to select their type.
/// </summary>
/// <seealso cref="BorgSelectTypeUserInterface"/>
/// <seealso cref="BorgSwitchableTypeComponent"/>
[GenerateTypedNameReferences]
public sealed partial class BorgSelectTypeMenu : FancyWindow
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;

private BorgTypePrototype? _selectedBorgType;

public event Action<ProtoId<BorgTypePrototype>>? ConfirmedBorgType;
public event Action<BorgSubtypePrototype?>? ConfirmedBorgSubtype; // CD event - borg subtypes

[ValidatePrototypeId<GuideEntryPrototype>]
private static readonly List<ProtoId<GuideEntryPrototype>> GuidebookEntries = new() { "Cyborgs", "Robotics" };

public BorgSelectTypeMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);

var group = new ButtonGroup();
foreach (var borgType in _prototypeManager.EnumeratePrototypes<BorgTypePrototype>().OrderBy(PrototypeName))
{
var button = new Button
{
Text = PrototypeName(borgType),
Group = group,
};
button.OnPressed += _ =>
{
_selectedBorgType = borgType;
UpdateInformation(borgType);
};
SelectionsContainer.AddChild(button);
}

ConfirmTypeButton.OnPressed += ConfirmButtonPressed;
HelpGuidebookIds = GuidebookEntries;

// CD - borg subtype
ChassisSpriteSelection.SubtypeSelected += () =>
{
ConfirmTypeButton.Disabled = false;
};
}

private void UpdateInformation(BorgTypePrototype prototype)
{
_selectedBorgType = prototype;

InfoContents.Visible = true;
InfoPlaceholder.Visible = false;

NameLabel.Text = PrototypeName(prototype);
DescriptionLabel.Text = Loc.GetString($"borg-type-{prototype.ID}-desc");
ChassisView.SetPrototype(prototype.DummyPrototype);

// CD changes below
if (_selectedBorgType != null)
{
ConfirmTypeButton.Disabled = true;
ChassisSpriteSelection.Update(_selectedBorgType);
}
}

private void ConfirmButtonPressed(BaseButton.ButtonEventArgs obj)
{
if (_selectedBorgType == null)
return;

ConfirmedBorgSubtype?.Invoke(ChassisSpriteSelection.SubtypePrototype); // CD - borg subtype
ConfirmedBorgType?.Invoke(_selectedBorgType);
}

private static string PrototypeName(BorgTypePrototype prototype)
{
return Loc.GetString($"borg-type-{prototype.ID}-name");
}
}
33 changes: 33 additions & 0 deletions Content.Client/Silicons/Borgs/BorgSelectTypeUserInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Content.Shared._CD.Silicons;
using Content.Shared._CD.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components;
using JetBrains.Annotations;
using Robust.Client.UserInterface;

namespace Content.Client.Silicons.Borgs;

/// <summary>
/// User interface used by borgs to select their type.
/// </summary>
/// <seealso cref="BorgSelectTypeMenu"/>
/// <seealso cref="BorgSwitchableTypeComponent"/>
/// <seealso cref="BorgSwitchableTypeUiKey"/>
[UsedImplicitly]
public sealed class BorgSelectTypeUserInterface : BoundUserInterface
{
[ViewVariables]
private BorgSelectTypeMenu? _menu;

public BorgSelectTypeUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}

protected override void Open()
{
base.Open();

_menu = this.CreateWindow<BorgSelectTypeMenu>();
_menu.ConfirmedBorgType += prototype => SendPredictedMessage(new BorgSelectTypeMessage(prototype));
_menu.ConfirmedBorgSubtype += subtypePrototype => SendPredictedMessage(new BorgSelectSubtypeMessage(subtypePrototype?.ID)); // CD - borg subtypes
}
}
97 changes: 97 additions & 0 deletions Content.Client/Silicons/Borgs/BorgSwitchableTypeSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using Content.Shared._CD.Silicons.Borgs;
using Content.Shared.Movement.Components;
using Content.Shared.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components;
using Robust.Client.GameObjects;
using Robust.Client.ResourceManagement;
using Robust.Shared.Serialization.TypeSerializers.Implementations;

namespace Content.Client.Silicons.Borgs;

/// <summary>
/// Client side logic for borg type switching. Sets up primarily client-side visual information.
/// </summary>
/// <seealso cref="SharedBorgSwitchableTypeSystem"/>
/// <seealso cref="BorgSwitchableTypeComponent"/>
public sealed class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeSystem
{
[Dependency] private readonly BorgSystem _borgSystem = default!;
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<BorgSwitchableTypeComponent, AfterAutoHandleStateEvent>(AfterStateHandler);
SubscribeLocalEvent<BorgSwitchableTypeComponent, ComponentStartup>(OnComponentStartup);
}

private void OnComponentStartup(Entity<BorgSwitchableTypeComponent> ent, ref ComponentStartup args)
{
UpdateEntityAppearance(ent);
}

private void AfterStateHandler(Entity<BorgSwitchableTypeComponent> ent, ref AfterAutoHandleStateEvent args)
{
UpdateEntityAppearance(ent);
}

protected override void UpdateEntityAppearance(
Entity<BorgSwitchableTypeComponent> entity,
BorgTypePrototype prototype)
{
// CD - added checks to stop sprite state errors
if (!TryComp<BorgSwitchableSubtypeComponent>(entity, out var subtype) ||
subtype.BorgSubtype != null)
return;

if (TryComp(entity, out SpriteComponent? sprite))
{
if (_resourceCache.TryGetResource<RSIResource>(
SpriteSpecifierSerializer.TextureRoot / prototype.SpritePath,
out var res))
{
sprite.BaseRSI = res.RSI;
}
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.Body, prototype.SpriteBodyState);
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.LightStatus, prototype.SpriteToggleLightState);
}

if (TryComp(entity, out BorgChassisComponent? chassis))
{
_borgSystem.SetMindStates(
(entity.Owner, chassis),
prototype.SpriteHasMindState,
prototype.SpriteNoMindState);

if (TryComp(entity, out AppearanceComponent? appearance))
{
// Queue update so state changes apply.
_appearance.QueueUpdate(entity, appearance);
}
}

if (prototype.SpriteBodyMovementState is { } movementState)
{
var spriteMovement = EnsureComp<SpriteMovementComponent>(entity);
spriteMovement.NoMovementLayers.Clear();
spriteMovement.NoMovementLayers["movement"] = new PrototypeLayerData
{
State = prototype.SpriteBodyState,
};
spriteMovement.MovementLayers.Clear();
spriteMovement.MovementLayers["movement"] = new PrototypeLayerData
{
State = movementState,
};
}
else
{
RemComp<SpriteMovementComponent>(entity);
}

base.UpdateEntityAppearance(entity, prototype);
}
}
Loading
Loading