Skip to content
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
29 changes: 29 additions & 0 deletions Content.Client/_LuaM/Yupi/YupiUi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Content.Client.UserInterface.Fragments;
using Content.Shared._LuaM.Yupi;
using Content.Shared.CartridgeLoader;
using Robust.Client.UserInterface;

namespace Content.Client._LuaM.Yupi;

public sealed partial class YupiUi : UIFragment
{
private YupiUiFragment? _fragment;

public override Control GetUIFragmentRoot()
{
return _fragment!;
}

public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
{
_fragment = new YupiUiFragment();
_fragment.OnSend += (code, amount) =>
userInterface.SendMessage(new CartridgeUiMessage(new YupiTransferMessage(code, amount)));
}

public override void UpdateState(BoundUserInterfaceState state)
{
if (state is YupiUiState yupiState)
_fragment?.UpdateState(yupiState);
}
}
49 changes: 49 additions & 0 deletions Content.Client/_LuaM/Yupi/YupiUiFragment.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<yupi:YupiUiFragment xmlns:yupi="clr-namespace:Content.Client._LuaM.Yupi"
xmlns="https://spacestation14.io"
Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" HScrollEnabled="False">
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="5">
<Label Name="NoAccountLabel"
Text="{Loc 'yupi-no-account'}"
HorizontalAlignment="Center"
Margin="0 20"
Visible="False"/>

<BoxContainer Name="AccountBox" Orientation="Vertical" HorizontalExpand="True" Visible="False">
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'yupi-ui-own-code'}" Margin="0 0 5 0"/>
<Button Name="CopyCodeButton" Text="------" ToolTip="{Loc 'yupi-ui-copy-tooltip'}"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'yupi-ui-balance'}" Margin="0 0 5 0"/>
<Label Name="BalanceLabel"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'yupi-ui-low-rate-remaining'}" Margin="0 0 5 0"/>
<Label Name="LowRateLabel"/>
</BoxContainer>

<BoxContainer Orientation="Horizontal" Margin="0 10 0 0">
<Label Text="{Loc 'yupi-ui-target-code'}" MinWidth="120"/>
<LineEdit Name="CodeInput" PlaceHolder="H2U2D8" HorizontalExpand="True"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'yupi-ui-amount'}" MinWidth="120"/>
<LineEdit Name="AmountInput" PlaceHolder="1000" HorizontalExpand="True"/>
</BoxContainer>
<RichTextLabel Name="CommissionLabel" HorizontalExpand="True"/>

<Button Name="SendButton" Text="{Loc 'yupi-ui-send'}" Margin="0 5 0 0" Disabled="True"/>
</BoxContainer>

<BoxContainer Orientation="Vertical" HorizontalAlignment="Center" Margin="0 8 0 0">
<TextureRect TexturePath="/Textures/_LuaM/Yupi/bank_logo.png"
Stretch="KeepAspectCentered"
SetSize="96 96"/>
<Label Text="{Loc 'yupi-bank-name'}" HorizontalAlignment="Center" StyleClasses="LabelSubText"/>
</BoxContainer>
</BoxContainer>
</ScrollContainer>
</yupi:YupiUiFragment>
131 changes: 131 additions & 0 deletions Content.Client/_LuaM/Yupi/YupiUiFragment.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using Content.Shared._LuaM.Yupi;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
using Robust.Shared.Utility;

namespace Content.Client._LuaM.Yupi;

[GenerateTypedNameReferences]
public sealed partial class YupiUiFragment : BoxContainer
{
[Dependency] private IClipboardManager _clipboard = default!;
[Dependency] private IGameTiming _timing = default!;

private static readonly Color HintColor = Color.FromHex("#A0A0A0");

public event Action<string, int>? OnSend;

private int _balance;
private int _lowRateRemaining;
private TimeSpan _nextTransfer;

private int _shownCooldownSeconds = -1;

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

CodeInput.IsValid = text => text.Length <= YupiRules.CodeLength;
AmountInput.IsValid = text => text.Length == 0 || TryParseAmount(text, out var value) && value >= 0;

CodeInput.OnTextChanged += _ => UpdatePreview();
AmountInput.OnTextChanged += _ => UpdatePreview();

CopyCodeButton.OnPressed += _ =>
{
if (YupiRules.IsValidCode(CopyCodeButton.Text ?? string.Empty))
_clipboard.SetText(CopyCodeButton.Text!);
};

SendButton.OnPressed += _ =>
{
if (TryGetInput(out var code, out var amount))
OnSend?.Invoke(code, amount);
};

UpdatePreview();
}

public void UpdateState(YupiUiState state)
{
var hasAccount = state.OwnCode != string.Empty;
NoAccountLabel.Visible = !hasAccount;
AccountBox.Visible = hasAccount;

CopyCodeButton.Text = hasAccount ? state.OwnCode : "------";
BalanceLabel.Text = Loc.GetString("yupi-ui-credits", ("amount", YupiRules.FormatCredits(state.Balance)));
LowRateLabel.Text = Loc.GetString("yupi-ui-credits", ("amount", YupiRules.FormatCredits(state.LowRateRemaining)));
_balance = state.Balance;
_lowRateRemaining = state.LowRateRemaining;
_nextTransfer = state.NextTransfer;
_shownCooldownSeconds = -1;
UpdatePreview();
}

protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);

var seconds = GetCooldownSeconds();
if (seconds == _shownCooldownSeconds)
return;

_shownCooldownSeconds = seconds;
UpdateSendButton();
}

private void UpdatePreview()
{
string text;
if (!TryParseAmount(AmountInput.Text, out var amount) || amount <= 0)
{
text = Loc.GetString("yupi-ui-commission-hint",
("low", YupiRules.LowRatePercent),
("high", YupiRules.HighRatePercent));
}
else
{
var commission = YupiRules.GetCommission(amount, _lowRateRemaining);
text = Loc.GetString("yupi-ui-commission-preview",
("commission", YupiRules.FormatCredits(commission)),
("net", YupiRules.FormatCredits(amount - commission)));
}

CommissionLabel.SetMessage(FormattedMessage.FromUnformatted(text), HintColor);
UpdateSendButton();
}

private void UpdateSendButton()
{
var seconds = GetCooldownSeconds();
SendButton.Text = seconds > 0
? Loc.GetString("yupi-ui-send-cooldown", ("time", $"{seconds / 60}:{seconds % 60:D2}"))
: Loc.GetString("yupi-ui-send");
SendButton.Disabled = seconds > 0 || !TryGetInput(out _, out _);
}

private int GetCooldownSeconds()
{
var remaining = _nextTransfer - _timing.CurTime;
return remaining > TimeSpan.Zero ? (int) Math.Ceiling(remaining.TotalSeconds) : 0;
}

private bool TryGetInput(out string code, out int amount)
{
code = YupiRules.NormalizeCode(CodeInput.Text);
return TryParseAmount(AmountInput.Text, out amount)
&& amount > 0
&& amount <= _balance
&& amount > YupiRules.GetCommission(amount, _lowRateRemaining)
&& YupiRules.IsValidCode(code);
}

private static bool TryParseAmount(string text, out int amount)
{
return int.TryParse(text.Replace(".", string.Empty), out amount);
}
}
141 changes: 141 additions & 0 deletions Content.Server/_LuaM/Yupi/YupiCartridgeSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using Content.Server._NF.Bank;
using Content.Server.CartridgeLoader;
using Content.Shared._LuaM.Yupi;
using Content.Shared._NF.Bank.Components;
using Content.Shared.CartridgeLoader;
using Content.Shared.Popups;
using Robust.Shared.Containers;
using Robust.Shared.Player;

namespace Content.Server._LuaM.Yupi;

public sealed partial class YupiCartridgeSystem : EntitySystem
{
[Dependency] private BankSystem _bank = default!;
[Dependency] private CartridgeLoaderSystem _cartridgeLoader = default!;
[Dependency] private SharedContainerSystem _container = default!;
[Dependency] private ISharedPlayerManager _player = default!;
[Dependency] private SharedPopupSystem _popup = default!;
[Dependency] private YupiSystem _yupi = default!;

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

SubscribeLocalEvent<YupiCartridgeComponent, CartridgeUiReadyEvent>(OnUiReady);
SubscribeLocalEvent<YupiCartridgeComponent, CartridgeMessageEvent>(OnMessage);
}

private void OnUiReady(Entity<YupiCartridgeComponent> ent, ref CartridgeUiReadyEvent args)
{
UpdateUi(args.Loader);
}

private void OnMessage(Entity<YupiCartridgeComponent> ent, ref CartridgeMessageEvent args)
{
if (args is not YupiTransferMessage message)
return;

var loader = GetEntity(args.LoaderUid);

if (!TryGetHolder(loader, out var holder) || holder != args.Actor)
return;

var result = _yupi.TryTransfer(holder, message.TargetCode, message.Amount, out var target, out var commission, out var received);
if (result == YupiTransferResult.Success)
{
var net = message.Amount - commission;
_popup.PopupEntity(Loc.GetString("yupi-transfer-sent",
("amount", YupiRules.FormatCredits(message.Amount)),
("code", _yupi.GetOrCreateCode(target)),
("commission", YupiRules.FormatCredits(commission)),
("net", YupiRules.FormatCredits(net))),
holder,
holder);

var savings = net - received;
_popup.PopupEntity(Loc.GetString("yupi-transfer-received",
("amount", YupiRules.FormatCredits(received)),
("code", _yupi.GetOrCreateCode(holder)),
("hasSavings", savings > 0 ? "yes" : "no"),
("savings", YupiRules.FormatCredits(savings))),
target,
target);

RefreshOpenApps(target);
}
else
{
_popup.PopupEntity(Loc.GetString(GetErrorLoc(result)), holder, holder, PopupType.SmallCaution);
}

UpdateUi(loader);
}

private void UpdateUi(EntityUid loader)
{
var code = string.Empty;
var balance = 0;
var lowRateRemaining = 0;
var nextTransfer = TimeSpan.Zero;

if (TryGetHolder(loader, out var holder))
{
code = _yupi.GetOrCreateCode(holder);
_bank.TryGetBalance(holder, out balance);
if (_player.TryGetSessionByEntity(holder, out var session))
{
lowRateRemaining = _yupi.GetLowRateRemaining(session.UserId);
nextTransfer = _yupi.GetNextTransferTime(session.UserId);
}
}

_cartridgeLoader.UpdateCartridgeUiState(loader, new YupiUiState(code, balance, lowRateRemaining, nextTransfer));
}

private void RefreshOpenApps(EntityUid holder)
{
var query = EntityQueryEnumerator<CartridgeLoaderComponent>();
while (query.MoveNext(out var loader, out var loaderComp))
{
if (!HasComp<YupiCartridgeComponent>(loaderComp.ActiveProgram)
|| !TryGetHolder(loader, out var loaderHolder)
|| loaderHolder != holder)
continue;

UpdateUi(loader);
}
}

private bool TryGetHolder(EntityUid loader, out EntityUid holder)
{
var current = loader;
while (_container.TryGetContainingContainer((current, null, null), out var container))
{
current = container.Owner;
if (!HasComp<BankAccountComponent>(current))
continue;

holder = current;
return true;
}

holder = default;
return false;
}

private static string GetErrorLoc(YupiTransferResult result)
{
return result switch
{
YupiTransferResult.InvalidAmount => "yupi-error-invalid-amount",
YupiTransferResult.InvalidTarget => "yupi-error-invalid-target",
YupiTransferResult.SelfTransfer => "yupi-error-self-transfer",
YupiTransferResult.NoAccount => "yupi-no-account",
YupiTransferResult.TargetUnavailable => "yupi-error-target-unavailable",
YupiTransferResult.InsufficientFunds => "yupi-error-insufficient-funds",
YupiTransferResult.Cooldown => "yupi-error-cooldown",
_ => "yupi-error-disabled",
};
}
}
13 changes: 13 additions & 0 deletions Content.Server/_LuaM/Yupi/YupiComponents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Content.Server._LuaM.Yupi;

[RegisterComponent, Access(typeof(YupiSystem))]
public sealed partial class YupiAccountComponent : Component
{
[ViewVariables]
public string Code = string.Empty;
}

[RegisterComponent]
public sealed partial class YupiCartridgeComponent : Component
{
}
Loading
Loading