Привязка банковского аккаунта (#506)

* Sustenance vend price fix

* Account link
This commit is contained in:
Aviu00
2023-10-23 21:23:33 +09:00
committed by Aviu00
parent b1ed258348
commit 6bd97b4987
18 changed files with 265 additions and 66 deletions

View File

@@ -1,10 +1,13 @@
using Content.Client.UserInterface.Fragments;
using Content.Shared.CartridgeLoader;
using Content.Shared.White.Economy;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
namespace Content.Client.White.Economy.Ui;
[UsedImplicitly]
public sealed partial class BankUi : UIFragment
{
private BankUiFragment? _fragment;
@@ -17,6 +20,8 @@ public sealed partial class BankUi : UIFragment
public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
{
_fragment = new BankUiFragment();
_fragment.OnLinkAttempt += message => userInterface.SendMessage(new CartridgeUiMessage(message));
}
public override void UpdateState(BoundUserInterfaceState state)

View File

@@ -1,12 +1,30 @@
<ui:BankUiFragment xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.White.Economy.Ui">
xmlns:ui="clr-namespace:Content.Client.White.Economy.Ui"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
<PanelContainer StyleClasses="BackgroundDark"></PanelContainer>
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="8">
<BoxContainer Name="LinkedAccount" Orientation="Vertical" Visible="False">
<RichTextLabel Name="LinkedAccountNameLabel"/>
<RichTextLabel Name="LinkedAccountBalanceLabel"/>
<RichTextLabel Name="LinkedAccountNumberLabel" />
<RichTextLabel Name="LinkedAccountNameLabel" />
<RichTextLabel Name="LinkedAccountBalanceLabel" />
</BoxContainer>
<RichTextLabel Name="NoLinkedAccountLabel" Visible="False"/>
<RichTextLabel Name="NoLinkedAccountLabel" Visible="False" />
<controls:StripeBack>
<Button Name="AccountLinkButton" HorizontalExpand="True" HorizontalAlignment="Center"
Text="{Loc 'bank-program-ui-link-account'}" />
<BoxContainer Name="AccountLink" Orientation="Vertical" Visible="False" HorizontalExpand="True"
Margin="0 5 0 5">
<RichTextLabel Name="AccountLinkMessageLabel" />
<GridContainer HorizontalExpand="True" Columns="2" Rows="2">
<LineEdit Name="AccountLineEdit" HorizontalExpand="True"
Text="{Loc 'bank-program-ui-account-number'}" />
<Button Name="LinkConfirmButton" Text="{Loc 'bank-program-ui-link-confirm'}" />
<LineEdit Name="PinLineEdit" HorizontalExpand="True" Text="PIN" />
<Button Name="LinkCancelButton" Text="{Loc 'bank-program-ui-link-cancel'}" />
</GridContainer>
</BoxContainer>
</controls:StripeBack>
<RichTextLabel Name="AccountLinkResultLabel" Visible="False" />
</BoxContainer>
</ui:BankUiFragment>

View File

@@ -1,30 +1,108 @@
using System.Text.RegularExpressions;
using Content.Client.Message;
using Content.Shared.White.Economy;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
namespace Content.Client.White.Economy.Ui;
[GenerateTypedNameReferences]
public sealed partial class BankUiFragment : BoxContainer
{
public Action<BankAccountLinkMessage>? OnLinkAttempt;
private bool _accountLinkActive;
private readonly string _lineEditPattern = "[^0-9]";
public BankUiFragment()
{
RobustXamlLoader.Load(this);
AccountLinkButton.OnPressed += _ =>
{
_accountLinkActive = true;
AccountLinkResultLabel.Visible = false;
UpdateAccountLinkUi();
};
LinkCancelButton.OnPressed += _ =>
{
_accountLinkActive = false;
UpdateAccountLinkUi();
};
PinLineEdit.OnTextChanged += _ =>
{
ValidateLineEdit(PinLineEdit, 4);
};
AccountLineEdit.OnTextChanged += _ =>
{
ValidateLineEdit(AccountLineEdit, 6);
};
LinkConfirmButton.OnPressed += _ =>
{
if (PinLineEdit.Text.Length != 4 || AccountLineEdit.Text.Length != 6)
return;
var accountId = int.Parse(AccountLineEdit.Text);
var pin = int.Parse(PinLineEdit.Text);
AccountLinkResultLabel.Visible = true;
_accountLinkActive = false;
OnLinkAttempt?.Invoke(new BankAccountLinkMessage(accountId, pin));
};
}
public void UpdateState(BankCartridgeUiState state)
{
LinkedAccount.Visible = state.AccountLinked;
NoLinkedAccountLabel.Visible = !state.AccountLinked;
var accountLinked = state.AccountId != null;
if (state.AccountLinked)
AccountLinkMessageLabel.SetMarkup(state.AccountLinkMessage);
AccountLinkResultLabel.SetMarkup(state.AccountLinkResult);
LinkedAccount.Visible = accountLinked;
NoLinkedAccountLabel.Visible = !accountLinked;
if (accountLinked)
{
LinkedAccountNameLabel.SetMarkup(Loc.GetString("eftpos-ui-account-text", ("owner", state.OwnerName)));
LinkedAccountNumberLabel.SetMarkup(Loc.GetString("bank-program-ui-account-number-text",
("account", state.AccountId!.Value)));
LinkedAccountNameLabel.SetMarkup(Loc.GetString("bank-program-ui-account-owner-text",
("owner", state.OwnerName)));
LinkedAccountBalanceLabel.SetMarkup(Loc.GetString("atm-ui-balance", ("balance", state.Balance)));
UpdateAccountLinkUi();
return;
}
NoLinkedAccountLabel.SetMarkup(Loc.GetString("bank-program-ui-no-account"));
UpdateAccountLinkUi();
}
private void UpdateAccountLinkUi()
{
AccountLinkButton.Visible = !_accountLinkActive;
AccountLink.Visible = _accountLinkActive;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
LinkConfirmButton.Disabled = PinLineEdit.Text.Length != 4 || AccountLineEdit.Text.Length != 6;
}
private void ValidateLineEdit(LineEdit lineEdit, int length)
{
var text = Regex.Replace(lineEdit.Text, _lineEditPattern, string.Empty);
if (text.Length > length)
{
text = text[..length];
}
lineEdit.Text = text;
}
}

View File

@@ -5,26 +5,22 @@
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label
Name="AmountLabel"
Text="{Loc 'eftpos-ui-amount-text'}" />
<LineEdit
Name="AmountLineEdit"
PlaceHolder="0"
MinWidth="60"
Access="Public" />
<Button
Name="CardButton"
Access="Public"
HorizontalExpand="True"
HorizontalAlignment="Right"
ToolTip="{Loc 'eftpos-ui-card-lock-desc'}"
Text="{Loc 'eftpos-ui-card-lock-text'}" />
<Label Name="AmountLabel"
Text="{Loc 'eftpos-ui-amount-text'}" />
<LineEdit Name="AmountLineEdit"
PlaceHolder="0"
MinWidth="60"
Access="Public" />
<Button Name="CardButton"
Access="Public"
HorizontalExpand="True"
HorizontalAlignment="Right"
ToolTip="{Loc 'eftpos-ui-card-lock-desc'}"
Text="{Loc 'eftpos-ui-card-lock-text'}" />
</BoxContainer>
<Label
Name="AccountLabel"
VerticalExpand="True"
VerticalAlignment="Bottom"
Access="Public" />
<Label Name="AccountLabel"
VerticalExpand="True"
VerticalAlignment="Bottom"
Access="Public" />
</BoxContainer>
</DefaultWindow>

View File

@@ -34,7 +34,7 @@ public sealed partial class EftposWindow : DefaultWindow
public void UpdateState(BoundUserInterfaceState state)
{
if(state is not EftposBuiState eftState)
if (state is not EftposBuiState eftState)
return;
AmountLineEdit.Text = eftState.Amount == 0 ? string.Empty : eftState.Amount.ToString();

View File

@@ -367,12 +367,12 @@ namespace Content.Server.VendingMachines
if (TryComp(item, out PdaComponent? pda) && pda.ContainedId is { Valid: true } id)
nextItem = id;
if (!TryComp<BankCardComponent>(nextItem, out var bankCard) || !bankCard.BankAccountId.HasValue
|| !_bankCard.TryGetAccount(bankCard.BankAccountId.Value, out var account)
if (!TryComp<BankCardComponent>(nextItem, out var bankCard) || !bankCard.AccountId.HasValue
|| !_bankCard.TryGetAccount(bankCard.AccountId.Value, out var account)
|| account.Balance < price)
continue;
_bankCard.TryChangeBalance(bankCard.BankAccountId.Value, -price);
_bankCard.TryChangeBalance(bankCard.AccountId.Value, -price);
success = true;
break;
}

View File

@@ -66,22 +66,22 @@ public sealed class ATMSystem : SharedATMSystem
var bankCard = Comp<BankCardComponent>(component.CardSlot.Item.Value);
var amount = stack.Count;
_bankCardSystem.TryChangeBalance(bankCard.BankAccountId!.Value, amount);
_bankCardSystem.TryChangeBalance(bankCard.AccountId!.Value, amount);
Del(args.Used);
_audioSystem.PlayPvs(component.SoundInsertCurrency, uid);
UpdateUiState(uid, _bankCardSystem.GetBalance(bankCard.BankAccountId.Value), true, Loc.GetString("atm-ui-select-withdraw-amount"));
UpdateUiState(uid, _bankCardSystem.GetBalance(bankCard.AccountId.Value), true, Loc.GetString("atm-ui-select-withdraw-amount"));
}
private void OnCardInserted(EntityUid uid, ATMComponent component, EntInsertedIntoContainerMessage args)
{
if (!TryComp<BankCardComponent>(args.Entity, out var bankCard) || !bankCard.BankAccountId.HasValue)
if (!TryComp<BankCardComponent>(args.Entity, out var bankCard) || !bankCard.AccountId.HasValue)
{
_container.EmptyContainer(args.Container);
return;
}
UpdateUiState(uid, _bankCardSystem.GetBalance(bankCard.BankAccountId.Value), true, Loc.GetString("atm-ui-select-withdraw-amount"));
UpdateUiState(uid, _bankCardSystem.GetBalance(bankCard.AccountId.Value), true, Loc.GetString("atm-ui-select-withdraw-amount"));
}
private void OnCardRemoved(EntityUid uid, ATMComponent component, EntRemovedFromContainerMessage args)
@@ -91,14 +91,14 @@ public sealed class ATMSystem : SharedATMSystem
private void OnWithdrawRequest(EntityUid uid, ATMComponent component, ATMRequestWithdrawMessage args)
{
if (!TryComp<BankCardComponent>(component.CardSlot.Item, out var bankCard) || !bankCard.BankAccountId.HasValue)
if (!TryComp<BankCardComponent>(component.CardSlot.Item, out var bankCard) || !bankCard.AccountId.HasValue)
{
if (component.CardSlot.ContainerSlot != null)
_container.EmptyContainer(component.CardSlot.ContainerSlot);
return;
}
if (!_bankCardSystem.TryGetAccount(bankCard.BankAccountId.Value, out var account) ||
if (!_bankCardSystem.TryGetAccount(bankCard.AccountId.Value, out var account) ||
account.AccountPin != args.Pin && !HasComp<EmaggedComponent>(uid))
{
_popupSystem.PopupEntity(Loc.GetString("atm-wrong-pin"), uid);

View File

@@ -12,7 +12,6 @@ public sealed class BankAccount
public string Name = string.Empty;
public EntityUid? CartridgeUid;
public EntityUid? LoaderUid;
public BankAccount(int accountId, int balance)
{

View File

@@ -3,6 +3,7 @@ using System.Linq;
using Content.Server.Access.Systems;
using Content.Server.Cargo.Components;
using Content.Server.Cargo.Systems;
using Content.Server.CartridgeLoader;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking;
using Content.Server.Roles.Jobs;
@@ -34,6 +35,7 @@ public sealed class BankCardSystem : EntitySystem
[Dependency] private readonly BankCartridgeSystem _bankCartridge = default!;
[Dependency] private readonly JobSystem _job = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly CartridgeLoaderSystem _cartridgeLoader = default!;
private const int SalaryDelay = 1200;
@@ -96,18 +98,17 @@ public sealed class BankCardSystem : EntitySystem
if (component.CommandBudgetCard &&
TryComp(_station.GetOwningStation(uid), out StationBankAccountComponent? acc))
{
component.BankAccountId = acc.BankAccount.AccountId;
component.AccountId = acc.BankAccount.AccountId;
return;
}
if (component.BankAccountId.HasValue)
if (component.AccountId.HasValue)
{
CreateAccount(component.BankAccountId.Value, component.StartingBalance);
CreateAccount(component.AccountId.Value, component.StartingBalance);
return;
}
var account = CreateAccount(default, component.StartingBalance);
component.BankAccountId = account.AccountId;
component.AccountId = account.AccountId;
}
private void OnRoundRestart(RoundRestartCleanupEvent ev)
@@ -122,7 +123,7 @@ public sealed class BankCardSystem : EntitySystem
var cardEntity = id.Owner;
var bankCardComponent = EnsureComp<BankCardComponent>(cardEntity);
if (!bankCardComponent.BankAccountId.HasValue || !TryGetAccount(bankCardComponent.BankAccountId.Value, out var bankAccount))
if (!bankCardComponent.AccountId.HasValue || !TryGetAccount(bankCardComponent.AccountId.Value, out var bankAccount))
return;
if (!TryComp(mind.Mind, out MindComponent? mindComponent))
@@ -130,25 +131,23 @@ public sealed class BankCardSystem : EntitySystem
bankAccount.Balance = GetSalary(mind.Mind) + 100;
mindComponent.AddMemory(new Memory("PIN", bankAccount.AccountPin.ToString()));
mindComponent.AddMemory(new Memory(Loc.GetString("character-info-memories-account-number"),
bankAccount.AccountId.ToString()));
bankAccount.Mind = (mind.Mind.Value, mindComponent);
bankAccount.Name = Name(ev.Mob);
if (!_inventorySystem.TryGetSlotEntity(ev.Mob, "id", out var pdaUid) ||
!TryComp(pdaUid, out CartridgeLoaderComponent? cartridgeLoader))
if (!_inventorySystem.TryGetSlotEntity(ev.Mob, "id", out var pdaUid))
return;
BankCartridgeComponent? comp = null;
var programs = new List<EntityUid>(cartridgeLoader.BackgroundPrograms);
if (cartridgeLoader.ActiveProgram != null)
programs.Add(cartridgeLoader.ActiveProgram.Value);
var programs = _cartridgeLoader.GetInstalled(pdaUid.Value);
var program = programs.Find(program => TryComp(program, out comp));
var program = programs.ToList().Find(program => TryComp(program, out comp));
if (comp == null)
return;
bankAccount.CartridgeUid = program;
bankAccount.LoaderUid = pdaUid;
comp.AccountId = bankAccount.AccountId;
}
}
@@ -219,8 +218,8 @@ public sealed class BankCardSystem : EntitySystem
}
account.Balance += amount;
if (account is {CartridgeUid: not null, LoaderUid: not null})
_bankCartridge.UpdateUiState(account.CartridgeUid.Value, account.LoaderUid.Value);
if (account.CartridgeUid != null)
_bankCartridge.UpdateUiState(account.CartridgeUid.Value);
return true;
}

View File

@@ -5,4 +5,9 @@ public sealed partial class BankCartridgeComponent : Component
{
[ViewVariables]
public int? AccountId;
[ViewVariables]
public EntityUid? Loader;
public string AccountLinkResult = string.Empty;
}

View File

@@ -1,5 +1,6 @@
using Content.Server.CartridgeLoader;
using Content.Shared.CartridgeLoader;
using Content.Shared.PDA;
using Content.Shared.White.Economy;
namespace Content.Server.White.Economy;
@@ -14,6 +15,51 @@ public sealed class BankCartridgeSystem : EntitySystem
base.Initialize();
SubscribeLocalEvent<BankCartridgeComponent, CartridgeMessageEvent>(OnUiMessage);
SubscribeLocalEvent<BankCartridgeComponent, CartridgeUiReadyEvent>(OnUiReady);
SubscribeLocalEvent<BankCartridgeComponent, CartridgeAddedEvent>(OnInstall);
SubscribeLocalEvent<BankCartridgeComponent, CartridgeRemovedEvent>(OnRemove);
}
private void OnRemove(EntityUid uid, BankCartridgeComponent component, CartridgeRemovedEvent args)
{
component.Loader = null;
}
private void OnInstall(EntityUid uid, BankCartridgeComponent component, CartridgeAddedEvent args)
{
component.Loader = args.Loader;
}
private void OnAccountLink(EntityUid uid, BankCartridgeComponent component, BankAccountLinkMessage args)
{
if (!_bankCardSystem.TryGetAccount(args.AccountId, out var account) || args.Pin != account.AccountPin ||
account.CommandBudgetAccount)
{
component.AccountLinkResult = Loc.GetString("bank-program-ui-link-error");
return;
}
component.AccountLinkResult = Loc.GetString("bank-program-ui-link-success");
if (args.AccountId != component.AccountId)
{
if (component.AccountId != null &&
_bankCardSystem.TryGetAccount(component.AccountId.Value, out var oldAccount) &&
oldAccount.CartridgeUid == uid)
oldAccount.CartridgeUid = null;
if (account.CartridgeUid != null)
Comp<BankCartridgeComponent>(account.CartridgeUid.Value).AccountId = null;
account.CartridgeUid = uid;
component.AccountId = args.AccountId;
}
if (!TryComp(GetEntity(args.LoaderUid), out PdaComponent? pda) || !pda.ContainedId.HasValue ||
HasComp<BankCardComponent>(pda.ContainedId.Value))
return;
var bankCard = AddComp<BankCardComponent>(pda.ContainedId.Value);
bankCard.AccountId = account.AccountId;
}
private void OnUiReady(EntityUid uid, BankCartridgeComponent component, CartridgeUiReadyEvent args)
@@ -23,24 +69,49 @@ public sealed class BankCartridgeSystem : EntitySystem
private void OnUiMessage(EntityUid uid, BankCartridgeComponent component, CartridgeMessageEvent args)
{
if (args is BankAccountLinkMessage message)
OnAccountLink(uid, component, message);
UpdateUiState(uid, GetEntity(args.LoaderUid), component);
}
public void UpdateUiState(EntityUid cartridgeUid, EntityUid loaderUid, BankCartridgeComponent? component = null)
private void UpdateUiState(EntityUid cartridgeUid, EntityUid loaderUid, BankCartridgeComponent? component = null)
{
if (!Resolve(cartridgeUid, ref component))
return;
var accountLinkMessage = Loc.GetString("bank-program-ui-link-program") + '\n';
if (TryComp(loaderUid, out PdaComponent? pda) && pda.ContainedId.HasValue)
{
accountLinkMessage += TryComp(pda.ContainedId.Value, out BankCardComponent? bankCard)
? Loc.GetString("bank-program-ui-link-id-card-linked", ("account", bankCard.AccountId!.Value))
: Loc.GetString("bank-program-ui-link-id-card");
}
else
{
accountLinkMessage += Loc.GetString("bank-program-ui-link-no-id-card");
}
var state = new BankCartridgeUiState
{
AccountLinked = component.AccountId != null
AccountLinkResult = component.AccountLinkResult,
AccountLinkMessage = accountLinkMessage
};
if (component.AccountId != null && _bankCardSystem.TryGetAccount(component.AccountId.Value, out var account))
{
state.Balance = account.Balance;
state.AccountId = account.AccountId;
state.OwnerName = account.Name;
}
_cartridgeLoaderSystem?.UpdateCartridgeUiState(loaderUid, state);
}
public void UpdateUiState(EntityUid cartridgeUid)
{
if (!TryComp(cartridgeUid, out BankCartridgeComponent? component) || component.Loader == null)
return;
UpdateUiState(cartridgeUid, component.Loader.Value, component);
}
}

View File

@@ -26,10 +26,10 @@ public sealed class EftposSystem : EntitySystem
private void OnInteractUsing(EntityUid uid, EftposComponent component, InteractUsingEvent args)
{
if (component.BankAccountId == null || !TryComp(args.Used, out BankCardComponent? bankCard) ||
bankCard.BankAccountId == component.BankAccountId || component.Amount <= 0 || bankCard.CommandBudgetCard)
bankCard.AccountId == component.BankAccountId || component.Amount <= 0 || bankCard.CommandBudgetCard)
return;
if (_bankCardSystem.TryChangeBalance(bankCard.BankAccountId!.Value, -component.Amount) &&
if (_bankCardSystem.TryChangeBalance(bankCard.AccountId!.Value, -component.Amount) &&
_bankCardSystem.TryChangeBalance(component.BankAccountId.Value, component.Amount))
{
_popupSystem.PopupEntity(Loc.GetString("eftpos-transaction-success"), uid);
@@ -50,10 +50,10 @@ public sealed class EftposSystem : EntitySystem
if (component.BankAccountId == null)
{
component.BankAccountId = bankCard.BankAccountId;
component.BankAccountId = bankCard.AccountId;
component.Amount = args.Amount;
}
else if (component.BankAccountId == bankCard.BankAccountId)
else if (component.BankAccountId == bankCard.AccountId)
{
component.BankAccountId = null;
component.Amount = 0;

View File

@@ -5,11 +5,11 @@ namespace Content.Shared.White.Economy;
[RegisterComponent, NetworkedComponent]
public sealed partial class BankCardComponent : Component
{
[DataField("accountId")]
public int? BankAccountId;
[DataField]
public int? AccountId;
[DataField("startingBalance")]
public int StartingBalance = 0;
public int StartingBalance;
[DataField("commandBudgetCard")]
public bool CommandBudgetCard;

View File

@@ -1,3 +1,4 @@
using Content.Shared.CartridgeLoader;
using Robust.Shared.Serialization;
namespace Content.Shared.White.Economy;
@@ -6,6 +7,21 @@ namespace Content.Shared.White.Economy;
public sealed class BankCartridgeUiState : BoundUserInterfaceState
{
public int Balance;
public int? AccountId = null;
public string OwnerName = string.Empty;
public bool AccountLinked;
public string AccountLinkMessage = string.Empty;
public string AccountLinkResult = string.Empty;
}
[Serializable, NetSerializable]
public sealed class BankAccountLinkMessage : CartridgeMessageEvent
{
public int AccountId;
public int Pin;
public BankAccountLinkMessage(int accountId, int pin)
{
AccountId = accountId;
Pin = pin;
}
}

View File

@@ -1,11 +1,9 @@
using Content.Shared.Containers.ItemSlots;
using Robust.Shared.Containers;
namespace Content.Shared.White.Economy;
public abstract class SharedATMSystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
public override void Initialize()

View File

@@ -1,5 +1,17 @@
bank-program-ui-no-account = [color=red]Аккаунт не привязан.[/color]
bank-program-name = Банк
bank-program-ui-link-account = Привязать аккаунт
bank-program-ui-account-number = Номер аккаунта
bank-program-ui-link-confirm = Привязать
bank-program-ui-link-cancel = Отмена
bank-program-ui-account-number-text = Аккаунт №{ $account }
bank-program-ui-account-owner-text = Владелец аккаунта: { $owner }
bank-program-ui-link-error = [color=red]Ошибка привязки аккаунта.[/color]
bank-program-ui-link-success = [color=green]Аккаунт успешно привязан.[/color]
bank-program-ui-link-program = Аккаунт будет привязан к приложению.
bank-program-ui-link-id-card = Аккаунт будет привязан к ID карте.
bank-program-ui-link-no-id-card = [color=red]ID карта отсутствует.[/color]
bank-program-ui-link-id-card-linked = [color=red]К ID карте уже привязан аккаунт: { $account }[/color]
ent-BankCartridge = Картридж банка
.desc = Программа для управления банковским аккаунтом.

View File

@@ -1,2 +1,3 @@
character-info-memories-label = Память
character-info-memories-placeholder-text = Воспоминания
character-info-memories-account-number = Аккаунт №

View File

@@ -952,6 +952,7 @@
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
priceMultiplier: 0.0
- type: Sprite
sprite: Structures/Machines/VendingMachines/sustenance.rsi
layers: