Skip to content

Commit

Permalink
Merge branch 'master' into pirates
Browse files Browse the repository at this point in the history
  • Loading branch information
Ratyyy authored Dec 18, 2024
2 parents 3726c38 + 77e193e commit ba3670c
Show file tree
Hide file tree
Showing 369 changed files with 22,074 additions and 259 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Content.Client._RMC14.Attachable.Systems;
using System.Numerics;

namespace Content.Client._RMC14.Attachable.Components;

[RegisterComponent, AutoGenerateComponentState]
[Access(typeof(AttachableHolderVisualsSystem))]
public sealed partial class AttachableHolderVisualsComponent : Component
{
/// <summary>
/// This dictionary contains a list of offsets for every slot that should display the attachable placed into it.
/// If a slot is not in this dictionary, the attachable inside will not be displayed.
/// The list of valid slot names can be found in AttachableHolderComponent.cs
/// </summary>
[DataField(required: true), AutoNetworkedField]
public Dictionary<string, Vector2> Offsets = new();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Content.Client._RMC14.Attachable.Systems;
using System.Numerics;
using Robust.Shared.Utility;

namespace Content.Client._RMC14.Attachable.Components;

[RegisterComponent, AutoGenerateComponentState]
[Access(typeof(AttachableHolderVisualsSystem))]
public sealed partial class AttachableVisualsComponent : Component
{
/// <summary>
/// Optional, only used if the item's own state should not be used.
/// The path to the RSI file that contains all the attached states.
/// </summary>
[DataField, AutoNetworkedField]
public ResPath? Rsi;

/// <summary>
/// Optional, only used if the item's own state should not be used.
/// This prefix is added to the name of the slot the attachable is installed in.
/// The prefix must be in kebab-case and end with a dash, like so: example-prefix-
/// The RSI must contain a state for every slot the attachable fits into.
/// If the attachment only fits into one slot, it should be named as follows: normal-state_suffix.
/// The slot names can be found in AttachableHolderComponent.cs
/// </summary>
[DataField, AutoNetworkedField]
public string? Prefix;

/// <summary>
/// Optional, only used if the item's own state should not be used.
/// This suffix is added to the name of the slot the attachable is installed in.
/// The RSI must contain a state for every slot the attachable fits into.
/// If the attachment only fits into one slot, it should be named as follows: normal-state_suffix.
/// The slot names can be found in AttachableHolderComponent.cs
/// </summary>
[DataField, AutoNetworkedField]
public string? Suffix = "_a";

/// <summary>
/// If true, will include the holder's slot name to find this attachment's state
/// in its RSI.
/// In this case, there must be a separate state for each slot the attachment fits into.
/// The states should be named as follows: prefix-slot-name-suffix.
/// </summary>
[DataField, AutoNetworkedField]
public bool IncludeSlotName;

/// <summary>
/// If this is toggled on and the item has an AttachableToggleableComponent, then the visualisation system will try to show a different sprite when it's active.
/// Each active state must have "-on" appended to the end of its name.
/// </summary>
[DataField, AutoNetworkedField]
public bool ShowActive;

/// <summary>
/// If this is set to true, the attachment will be redrawn on its holder every time it receives an AppearanceChangeEvent. Useful for things like the UGL.
/// </summary>
[DataField, AutoNetworkedField]
public bool RedrawOnAppearanceChange;

[DataField, AutoNetworkedField]
public int Layer;

[DataField, AutoNetworkedField]
public Vector2 Offset;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using Content.Client._RMC14.Attachable.Components;
using Content.Shared._RMC14.Attachable;
using Content.Shared._RMC14.Attachable.Components;
using Content.Shared._RMC14.Attachable.Events;
using Content.Shared._RMC14.Attachable.Systems;
using Robust.Client.GameObjects;
using Robust.Shared.Containers;

namespace Content.Client._RMC14.Attachable.Systems;

public sealed class AttachableHolderVisualsSystem : EntitySystem
{
[Dependency] private readonly AttachableHolderSystem _attachableHolderSystem = default!;

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

SubscribeLocalEvent<AttachableHolderVisualsComponent, EntRemovedFromContainerMessage>(OnDetached);
SubscribeLocalEvent<AttachableHolderVisualsComponent, AttachableHolderAttachablesAlteredEvent>(OnAttachablesAltered);

SubscribeLocalEvent<AttachableVisualsComponent, AppearanceChangeEvent>(OnAttachableAppearanceChange);
}

private void OnDetached(Entity<AttachableHolderVisualsComponent> holder, ref EntRemovedFromContainerMessage args)
{
if (!HasComp<AttachableVisualsComponent>(args.Entity) || !_attachableHolderSystem.HasSlot(holder.Owner, args.Container.ID))
return;

var holderEv = new AttachableHolderAttachablesAlteredEvent(args.Entity, args.Container.ID, AttachableAlteredType.Detached);
RaiseLocalEvent(holder, ref holderEv);
}

private void OnAttachablesAltered(Entity<AttachableHolderVisualsComponent> holder,
ref AttachableHolderAttachablesAlteredEvent args)
{
if (!TryComp(args.Attachable, out AttachableVisualsComponent? attachableComponent))
return;

string suffix = "";
if (attachableComponent.ShowActive && TryComp(args.Attachable, out AttachableToggleableComponent? toggleableComponent) && toggleableComponent.Active)
suffix = "-on";

var attachable = new Entity<AttachableVisualsComponent>(args.Attachable, attachableComponent);
switch (args.Alteration)
{
case AttachableAlteredType.Attached:
SetAttachableOverlay(holder, attachable, args.SlotId, suffix);
break;

case AttachableAlteredType.Detached:
RemoveAttachableOverlay(holder, args.SlotId);
break;

case AttachableAlteredType.Activated:
if (!attachableComponent.ShowActive)
break;

SetAttachableOverlay(holder, attachable, args.SlotId, suffix);
break;

case AttachableAlteredType.Deactivated:
if (!attachableComponent.ShowActive)
break;

SetAttachableOverlay(holder, attachable, args.SlotId, suffix);
break;

case AttachableAlteredType.Interrupted:
if (!attachableComponent.ShowActive)
break;

SetAttachableOverlay(holder, attachable, args.SlotId);
break;

case AttachableAlteredType.AppearanceChanged:
SetAttachableOverlay(holder, attachable, args.SlotId, suffix);
break;
}
}

private void RemoveAttachableOverlay(Entity<AttachableHolderVisualsComponent> holder, string slotId)
{
if (!holder.Comp.Offsets.ContainsKey(slotId) || !TryComp(holder, out SpriteComponent? spriteComponent))
return;

if (!spriteComponent.LayerMapTryGet(slotId, out var index))
return;

spriteComponent.LayerMapRemove(slotId);
spriteComponent.RemoveLayer(index);
}

private void SetAttachableOverlay(Entity<AttachableHolderVisualsComponent> holder,
Entity<AttachableVisualsComponent> attachable,
string slotId,
string suffix = "")
{
if (!holder.Comp.Offsets.ContainsKey(slotId) ||
!TryComp(holder, out SpriteComponent? holderSprite))
{
return;
}

if (!TryComp(attachable, out SpriteComponent? attachableSprite))
return;

var rsi = attachableSprite.LayerGetActualRSI(attachable.Comp.Layer)?.Path;
var state = attachableSprite.LayerGetState(attachable.Comp.Layer).ToString();
if (attachable.Comp.Rsi is { } rsiPath)
{
rsi = rsiPath;
}

if (!string.IsNullOrWhiteSpace(attachable.Comp.Prefix))
state = attachable.Comp.Prefix;

if (attachable.Comp.IncludeSlotName)
state += slotId;

if (!string.IsNullOrWhiteSpace(attachable.Comp.Suffix))
state += attachable.Comp.Suffix;

state += suffix;

var layerData = new PrototypeLayerData()
{
RsiPath = rsi.ToString(),
State = state,
Offset = holder.Comp.Offsets[slotId] + attachable.Comp.Offset,
Visible = true,
};

if (holderSprite.LayerMapTryGet(slotId, out var index))
{
holderSprite.LayerSetData(index, layerData);
return;
}

holderSprite.LayerMapSet(slotId, holderSprite.AddLayer(layerData));
}

private void OnAttachableAppearanceChange(Entity<AttachableVisualsComponent> attachable, ref AppearanceChangeEvent args)
{
if (!attachable.Comp.RedrawOnAppearanceChange ||
!_attachableHolderSystem.TryGetHolder(attachable.Owner, out var holderUid) ||
!_attachableHolderSystem.TryGetSlotId(holderUid.Value, attachable.Owner, out var slotId))
{
return;
}

var holderEvent = new AttachableHolderAttachablesAlteredEvent(attachable.Owner, slotId, AttachableAlteredType.AppearanceChanged);
RaiseLocalEvent(holderUid.Value, ref holderEvent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'rmc-attachable-holder-strip-ui-title'}"
MinSize="200 100"
SetSize="200 100">
<BoxContainer Name="SlotsContainer" Orientation="Vertical" VerticalExpand="True"
HorizontalExpand="True" Margin="8 8 8 8" />
</controls:FancyWindow>
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using Content.Client.UserInterface.Controls;
using Content.Shared._RMC14.Attachable;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;

namespace Content.Client.ADT._RMC14.Attachable.Ui;

[GenerateTypedNameReferences]
public sealed partial class AttachableHolderChooseSlotMenu : FancyWindow
{
private readonly AttachmentChooseSlotBui _boundUI;
private readonly Dictionary<string, AttachableSlotControl> _attachableSlotControls;

public AttachableHolderChooseSlotMenu(AttachmentChooseSlotBui boundUI)
{
RobustXamlLoader.Load(this);

_boundUI = boundUI;
_attachableSlotControls = new Dictionary<string, AttachableSlotControl>();
OnClose += boundUI.Close;
}

public void UpdateMenu(List<string> attachableSlots)
{
foreach (var slotId in attachableSlots)
{
if (!_attachableSlotControls.ContainsKey(slotId))
AddSlotControl(slotId);
}
}

private void AddSlotControl(string slotId)
{
var slotControl = new AttachableSlotControl(this, _boundUI, slotId);
SlotsContainer.AddChild(slotControl);
_attachableSlotControls.Add(slotId, slotControl);
}

private sealed class AttachableSlotControl : Control
{
public AttachableSlotControl(AttachableHolderChooseSlotMenu slotMenu,
AttachmentChooseSlotBui boundUI,
string slotId)
{
var button = new Button { Text = Loc.GetString(slotId) };

button.OnPressed += _ =>
{
boundUI.SendMessage(new AttachableHolderAttachToSlotMessage(slotId));
slotMenu.Close();
};

AddChild(button);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'rmc-attachable-holder-strip-ui-title'}"
MinSize="350 400"
SetSize="350 400">
<BoxContainer Name="AttachablesContainer" Orientation="Vertical" VerticalExpand="True"
Margin="8 8 8 8" />
</controls:FancyWindow>
Loading

0 comments on commit ba3670c

Please sign in to comment.