Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Borg type switching. #32586

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
Jump to file
Failed to load files.
Loading
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 @@ -58,6 +58,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 @@ -70,11 +71,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 (args.Sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var layer))
Expand All @@ -84,6 +81,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 @@ -235,9 +235,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
3 changes: 2 additions & 1 deletion 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 Expand Up @@ -102,7 +103,7 @@ protected virtual void OnRefreshComponentHud(EntityUid uid, T component, Refresh
args.Components.Add(component);
}

private void RefreshOverlay(EntityUid uid)
protected void RefreshOverlay(EntityUid uid)
{
if (uid != _player.LocalSession?.AttachedEntity)
return;
Expand Down
7 changes: 7 additions & 0 deletions Content.Client/Overlays/ShowHealthBarsSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@ public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<ShowHealthBarsComponent, AfterAutoHandleStateEvent>(OnHandleState);

_overlay = new(EntityManager, _prototype);
}

private void OnHandleState(Entity<ShowHealthBarsComponent> ent, ref AfterAutoHandleStateEvent args)
{
RefreshOverlay(ent);
}

protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component)
{
base.UpdateInternal(component);
Expand Down
7 changes: 7 additions & 0 deletions Content.Client/Overlays/ShowHealthIconsSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
{
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;

[ViewVariables]
public HashSet<string> DamageContainers = new();

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

SubscribeLocalEvent<DamageableComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
SubscribeLocalEvent<ShowHealthIconsComponent, AfterAutoHandleStateEvent>(OnHandleState);
}

protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthIconsComponent> component)
Expand All @@ -43,6 +45,11 @@ protected override void DeactivateInternal()
DamageContainers.Clear();
}

private void OnHandleState(Entity<ShowHealthIconsComponent> ent, ref AfterAutoHandleStateEvent args)
{
RefreshOverlay(ent);
}

private void OnGetStatusIconsEvent(Entity<DamageableComponent> entity, ref GetStatusIconsEvent args)
{
if (!IsActive)
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 @@ -131,7 +131,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;
}
}

42 changes: 42 additions & 0 deletions Content.Client/Silicons/Borgs/BorgSelectTypeMenu.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
Title="{Loc 'borg-select-type-menu-title'}"
SetSize="550 300">
<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>
<controls:ConfirmButton Name="ConfirmTypeButton" Text="{Loc 'borg-select-type-menu-confirm'}"
Disabled="True" HorizontalAlignment="Right" />
</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>
81 changes: 81 additions & 0 deletions Content.Client/Silicons/Borgs/BorgSelectTypeMenu.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Linq;
using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Systems.Guidebook;
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;

[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;
}

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

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

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

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

ConfirmedBorgType?.Invoke(_selectedBorgType);
}

private static string PrototypeName(BorgTypePrototype prototype)
{
return Loc.GetString($"borg-type-{prototype.ID}-name");
}
}
30 changes: 30 additions & 0 deletions Content.Client/Silicons/Borgs/BorgSelectTypeUserInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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 => SendMessage(new BorgSelectTypeMessage(prototype));
}
}
81 changes: 81 additions & 0 deletions Content.Client/Silicons/Borgs/BorgSwitchableTypeSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using Content.Shared.Movement.Components;
using Content.Shared.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components;
using Robust.Client.GameObjects;

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!;

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)
{
if (TryComp(entity, out SpriteComponent? sprite))
{
sprite.LayerSetState(BorgVisualLayers.Body, prototype.SpriteBodyState);
sprite.LayerSetState(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