Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Content.Client.UserInterface.Controls;
using Content.Shared._WF.SafetyDepositBox.BUI;
using Content.Shared._WF.SafetyDepositBox.Events;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;

namespace Content.Client._WF.SafetyDepositBox;

public sealed class SafetyDepositConsoleBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private SafetyDepositConsoleWindow? _window;

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

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

_window = this.CreateWindow<SafetyDepositConsoleWindow>();
_window.Title = Loc.GetString("safety-deposit-console-title");
_window.OnPurchasePressed += OnPurchasePressed;
_window.OnDepositPressed += OnDepositPressed;
_window.OnWithdrawPressed += OnWithdrawPressed;
_window.OnReclaimPressed += OnReclaimPressed;
}

private void OnPurchasePressed(EntProtoId boxSize)
{
SendMessage(new SafetyDepositPurchaseMessage(boxSize));
}

private void OnDepositPressed()
{
SendMessage(new SafetyDepositDepositMessage());
}

private void OnWithdrawPressed(Guid boxId)
{
SendMessage(new SafetyDepositWithdrawMessage(boxId));
}

private void OnReclaimPressed(Guid boxId)
{
SendMessage(new SafetyDepositReclaimMessage(boxId));
}

protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);

if (state is not SafetyDepositConsoleState castState)
return;

_window?.UpdateState(castState);
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);

if (disposing)
_window?.Dispose();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
MinSize="650 550"
Title="{Loc 'safety-deposit-console-title'}">
<BoxContainer Orientation="Vertical" Margin="15">
<!-- Header -->
<Label Text="{Loc 'safety-deposit-console-header'}"
StyleClasses="LabelHeading"
Margin="0 0 0 15"
HorizontalAlignment="Center"/>

<!-- Top Action Cells -->
<BoxContainer Orientation="Horizontal" Margin="0 0 0 15" HorizontalExpand="True">
<!-- Purchase Box Cell -->
<PanelContainer StyleClasses="AngleRect" HorizontalExpand="True" Margin="0 0 5 0">
<BoxContainer Orientation="Vertical" Margin="15">
<Label Text="{Loc 'safety-deposit-console-purchase-section'}"
StyleClasses="LabelSubHeading"
Margin="0 0 0 10"
HorizontalAlignment="Center"/>
<Label Text="{Loc 'safety-deposit-console-bank-payment-note'}"
Margin="0 0 0 10"
HorizontalAlignment="Center"
StyleClasses="LabelSecondaryColor LabelSubText"/>
<Button Name="PurchaseSmallButton"
Text="{Loc 'safety-deposit-console-purchase-small'}"
HorizontalAlignment="Stretch"
MinSize="0 30"
Margin="0 0 0 5"
StyleClasses="OpenBoth"/>
<Button Name="PurchaseMediumButton"
Text="{Loc 'safety-deposit-console-purchase-medium'}"
HorizontalAlignment="Stretch"
MinSize="0 30"
Margin="0 0 0 5"
StyleClasses="OpenBoth"/>
<Button Name="PurchaseLargeButton"
Text="{Loc 'safety-deposit-console-purchase-large'}"
HorizontalAlignment="Stretch"
MinSize="0 30"
StyleClasses="OpenBoth"/>
</BoxContainer>
</PanelContainer>

<!-- Deposit Box Cell -->
<PanelContainer StyleClasses="AngleRect" HorizontalExpand="True" Margin="5 0 0 0">
<BoxContainer Orientation="Vertical" Margin="15">
<Label Text="{Loc 'safety-deposit-console-deposit-section'}"
StyleClasses="LabelSubHeading"
Margin="0 0 0 10"
HorizontalAlignment="Center"/>
<Label Name="BoxSlotLabel"
Text="{Loc 'safety-deposit-console-no-box-in-slot'}"
Margin="0 0 0 10"
HorizontalAlignment="Center"
StyleClasses="LabelSecondaryColor"/>
<Button Name="DepositButton"
Text="{Loc 'safety-deposit-console-deposit-button'}"
HorizontalAlignment="Stretch"
MinSize="0 35"
StyleClasses="OpenBoth"/>
</BoxContainer>
</PanelContainer>
</BoxContainer>

<PanelContainer StyleClasses="AngleRect" MinSize="0 2" Margin="0 0 0 15"/>

<!-- Owned Boxes List -->
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<Label Text="{Loc 'safety-deposit-console-owned-boxes'}"
StyleClasses="LabelSubHeading"
Margin="0 0 0 10"/>
<PanelContainer StyleClasses="AngleRect" VerticalExpand="True" ModulateSelfOverride="#0A0A0A">
<ScrollContainer VerticalExpand="True" Margin="5">
<BoxContainer Name="OwnedBoxesContainer"
Orientation="Vertical"
VerticalExpand="True"
SeparationOverride="2"/>
</ScrollContainer>
</PanelContainer>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>
181 changes: 181 additions & 0 deletions Content.Client/_WF/SafetyDepositBox/SafetyDepositConsoleWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
using System.Linq;
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared._WF.SafetyDepositBox.BUI;
using Content.Shared._WF.SafetyDepositBox.Components;
using Content.Shared._WF.SafetyDepositBox.Events;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;

namespace Content.Client._WF.SafetyDepositBox;

[GenerateTypedNameReferences]
public sealed partial class SafetyDepositConsoleWindow : FancyWindow
{
public event Action<EntProtoId>? OnPurchasePressed;
public event Action? OnDepositPressed;
public event Action<Guid>? OnWithdrawPressed;
public event Action<Guid>? OnReclaimPressed;

public SafetyDepositConsoleWindow()
{
RobustXamlLoader.Load(this);

PurchaseSmallButton.OnPressed += _ => OnPurchasePressed?.Invoke("SafetyDepositBoxSmall");
PurchaseMediumButton.OnPressed += _ => OnPurchasePressed?.Invoke("SafetyDepositBoxMedium");
PurchaseLargeButton.OnPressed += _ => OnPurchasePressed?.Invoke("SafetyDepositBoxLarge");
DepositButton.OnPressed += _ => OnDepositPressed?.Invoke();
}

public void UpdateState(SafetyDepositConsoleState state)
{
// Update purchase buttons with costs

PurchaseSmallButton.Disabled = false; // Bank account check happens server-side
PurchaseSmallButton.Text = Loc.GetString("safety-deposit-console-purchase-small", ("cost", state.SmallBoxCost));

PurchaseMediumButton.Disabled = false;
PurchaseMediumButton.Text = Loc.GetString("safety-deposit-console-purchase-medium", ("cost", state.MediumBoxCost));

PurchaseLargeButton.Disabled = false;
PurchaseLargeButton.Text = Loc.GetString("safety-deposit-console-purchase-large", ("cost", state.LargeBoxCost));

// Update deposit button
DepositButton.Disabled = !state.HasBoxInSlot || state.BoxInSlot == null;

// Update box slot status
if (state.HasBoxInSlot && state.BoxInSlot != null)
{
BoxSlotLabel.Text = Loc.GetString("safety-deposit-console-box-in-slot",
("id", state.BoxInSlot.Value.BoxId.ToString()[..8]));
}
else
{
BoxSlotLabel.Text = Loc.GetString("safety-deposit-console-no-box-in-slot");
}

// Update owned boxes list
OwnedBoxesContainer.RemoveAllChildren();

if (state.OwnedBoxes.Count == 0)
{
var noBoxesLabel = new Label
{
Text = Loc.GetString("safety-deposit-console-no-boxes"),
StyleClasses = { "LabelSecondaryColor" },
HorizontalAlignment = Control.HAlignment.Center,
Margin = new Thickness(0, 20)
};
OwnedBoxesContainer.AddChild(noBoxesLabel);
}
else
{
// Sort boxes newest first (by BoxId descending)
var sortedBoxes = state.OwnedBoxes.OrderByDescending(b => b.BoxId).ToList();

var index = 0;
foreach (var box in sortedBoxes)
{
// Create a clean row similar to shipyard
var rowPanel = new PanelContainer
{
HorizontalExpand = true,
StyleClasses = { "AngleRect" },
// Alternate row colors for better readability
ModulateSelfOverride = index % 2 == 0 ? new Color(0.15f, 0.15f, 0.15f) : new Color(0.12f, 0.12f, 0.12f)
};

var rowContainer = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Horizontal,
HorizontalExpand = true,
Margin = new Thickness(8, 4)
};

// Box info label - shows nickname if available, otherwise box ID and size
var boxInfoText = !string.IsNullOrEmpty(box.Nickname)
? $"{box.Nickname}"
: $"Box {box.BoxId.ToString()[..8]}";

var boxIdLabel = new Label
{
Text = boxInfoText,
HorizontalExpand = true,
VerticalAlignment = Control.VAlignment.Center
};

// Status Label - fixed width, right aligned
string statusText;
Color statusColor;

if (box.IsDeposited)
{
statusText = Loc.GetString("safety-deposit-console-box-deposited");
statusColor = Color.LightGreen;
}
else if (box.LastWithdrawnRoundId.HasValue && box.LastWithdrawnRoundId.Value != state.CurrentRoundId)
{
// Box was withdrawn in a previous round - it's lost
statusText = Loc.GetString("safety-deposit-console-box-lost");
statusColor = Color.Red;
}
else
{
// Box was withdrawn in current round - it's in world
statusText = Loc.GetString("safety-deposit-console-box-in-world");
statusColor = Color.Yellow;
}

var statusLabel = new Label
{
Text = statusText,
MinWidth = 80,
Align = Label.AlignMode.Right,
Margin = new Thickness(0, 0, 5, 0),
HorizontalAlignment = Control.HAlignment.Right,
VerticalAlignment = Control.VAlignment.Center,
FontColorOverride = statusColor
};

// Determine if box is lost (withdrawn in previous round with no items)
var isLost = box.LastWithdrawnRoundId.HasValue &&
box.LastWithdrawnRoundId.Value != state.CurrentRoundId &&
!box.IsDeposited;

// Action button - either Withdraw or Reclaim depending on state
var actionButton = new Button
{
Text = isLost
? Loc.GetString("safety-deposit-console-reclaim-button")
: Loc.GetString("safety-deposit-console-withdraw-button"),
Disabled = !box.IsDeposited && !isLost,
MinWidth = 100,
HorizontalAlignment = Control.HAlignment.Right,
VerticalAlignment = Control.VAlignment.Center
};

var boxId = box.BoxId;
if (isLost)
{
actionButton.OnPressed += _ => OnReclaimPressed?.Invoke(boxId);
}
else
{
actionButton.OnPressed += _ => OnWithdrawPressed?.Invoke(boxId);
}

rowContainer.AddChild(boxIdLabel);
rowContainer.AddChild(statusLabel);
rowContainer.AddChild(actionButton);
rowPanel.AddChild(rowContainer);
OwnedBoxesContainer.AddChild(rowPanel);

index++;
}
}
}
}
Loading
Loading