diff --git a/Content.Client/ADT/Economy/ATMVisualLayers.cs b/Content.Client/ADT/Economy/ATMVisualLayers.cs
new file mode 100644
index 0000000000..b559aae509
--- /dev/null
+++ b/Content.Client/ADT/Economy/ATMVisualLayers.cs
@@ -0,0 +1,7 @@
+namespace Content.Client.ADT.Economy;
+
+public enum ATMVisualLayers : byte
+{
+ Base,
+ BaseUnshaded
+}
diff --git a/Content.Client/ADT/Economy/UI/ATMBui.cs b/Content.Client/ADT/Economy/UI/ATMBui.cs
new file mode 100644
index 0000000000..462d976cd6
--- /dev/null
+++ b/Content.Client/ADT/Economy/UI/ATMBui.cs
@@ -0,0 +1,42 @@
+using JetBrains.Annotations;
+
+namespace Content.Client.ADT.Economy.UI;
+
+
+[UsedImplicitly]
+public sealed class ATMBui : BoundUserInterface
+{
+ private AtmWindow _window;
+
+ public ATMBui(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+ _window = new AtmWindow();
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+ _window.OnClose += Close;
+ _window.OnWithdrawAttempt += SendMessage;
+
+ if (State != null)
+ {
+ UpdateState(State);
+ }
+
+ _window.OpenCentered();
+
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+ _window?.UpdateState(state);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ _window?.Close();
+ base.Dispose(disposing);
+ }
+}
diff --git a/Content.Client/ADT/Economy/UI/AtmWindow.xaml b/Content.Client/ADT/Economy/UI/AtmWindow.xaml
new file mode 100644
index 0000000000..a2a32b316a
--- /dev/null
+++ b/Content.Client/ADT/Economy/UI/AtmWindow.xaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/ADT/Economy/UI/AtmWindow.xaml.cs b/Content.Client/ADT/Economy/UI/AtmWindow.xaml.cs
new file mode 100644
index 0000000000..51490ae316
--- /dev/null
+++ b/Content.Client/ADT/Economy/UI/AtmWindow.xaml.cs
@@ -0,0 +1,92 @@
+using System.Text.RegularExpressions;
+using Content.Shared.ADT.Economy;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Timing;
+
+namespace Content.Client.ADT.Economy.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class AtmWindow : DefaultWindow
+{
+ public Action? OnWithdrawAttempt;
+
+ private readonly string _pinPattern = "[^0-9]";
+ public AtmWindow()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+
+ WithdrawButton.OnButtonDown += args =>
+ {
+ if(PinLineEdit.Text.Length != 4) return;
+ OnWithdrawAttempt?.Invoke(new ATMRequestWithdrawMessage(WithdrawSlider.Value, int.Parse((string) PinLineEdit.Text)));
+ };
+
+ PinLineEdit.OnTextChanged += _ =>
+ {
+ ValidatePin();
+ };
+ }
+
+ public void UpdateState(BoundUserInterfaceState state)
+ {
+ if(state is not ATMBuiState cast) return;
+
+ if (!cast.HasCard)
+ {
+ StatusLabel.Text = cast.InfoMessage;
+ BalanceLabel.Visible = false;
+ Divider.Visible = false;
+ StatusLabel.Visible = true;
+ WithdrawSlider.Visible = false;
+ PinLineEdit.Visible = false;
+ WithdrawButton.Visible = false;
+ return;
+ }
+
+ StatusLabel.Text = cast.InfoMessage;
+
+ BalanceLabel.Text = Loc.GetString("atm-ui-balance", ("balance", cast.AccountBalance));
+ BalanceLabel.Visible = true;
+
+ if (cast.AccountBalance > 0)
+ {
+ Divider.Visible = true;
+ StatusLabel.Visible = true;
+ WithdrawSlider.Visible = true;
+ PinLineEdit.Visible = true;
+ WithdrawButton.Visible = true;
+
+ WithdrawSlider.MaxValue = cast.AccountBalance;
+ WithdrawSlider.Value = Math.Min((int) WithdrawSlider.Value, cast.AccountBalance);
+ return;
+ }
+
+ Divider.Visible = false;
+ StatusLabel.Visible = false;
+ WithdrawSlider.Visible = false;
+ PinLineEdit.Visible = false;
+ WithdrawButton.Visible = false;
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ base.FrameUpdate(args);
+ WithdrawButton.Disabled = PinLineEdit.Text.Length != 4;
+ }
+
+ private void ValidatePin()
+ {
+ var pinText = Regex.Replace(PinLineEdit.Text, _pinPattern, string.Empty);
+
+ if (pinText.Length > 4)
+ {
+ pinText = pinText[..4];
+ }
+
+ PinLineEdit.Text = pinText;
+ }
+}
diff --git a/Content.Client/ADT/Economy/UI/BankUi.cs b/Content.Client/ADT/Economy/UI/BankUi.cs
new file mode 100644
index 0000000000..93721cc967
--- /dev/null
+++ b/Content.Client/ADT/Economy/UI/BankUi.cs
@@ -0,0 +1,33 @@
+using Content.Client.UserInterface.Fragments;
+using Content.Shared.ADT.Economy;
+using Content.Shared.CartridgeLoader;
+using JetBrains.Annotations;
+using Robust.Client.UserInterface;
+
+namespace Content.Client.ADT.Economy.UI;
+
+[UsedImplicitly]
+public sealed partial class BankUi : UIFragment
+{
+ private BankUiFragment? _fragment;
+
+ public override Control GetUIFragmentRoot()
+ {
+ return _fragment!;
+ }
+
+ 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)
+ {
+ if (state is not BankCartridgeUiState bankState)
+ return;
+
+ _fragment?.UpdateState(bankState);
+ }
+}
diff --git a/Content.Client/ADT/Economy/UI/BankUiFragment.xaml b/Content.Client/ADT/Economy/UI/BankUiFragment.xaml
new file mode 100644
index 0000000000..76976fe13f
--- /dev/null
+++ b/Content.Client/ADT/Economy/UI/BankUiFragment.xaml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/ADT/Economy/UI/BankUiFragment.xaml.cs b/Content.Client/ADT/Economy/UI/BankUiFragment.xaml.cs
new file mode 100644
index 0000000000..6bb2f66e5e
--- /dev/null
+++ b/Content.Client/ADT/Economy/UI/BankUiFragment.xaml.cs
@@ -0,0 +1,108 @@
+using System.Text.RegularExpressions;
+using Content.Client.Message;
+using Content.Shared.ADT.Economy;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Timing;
+
+namespace Content.Client.ADT.Economy.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class BankUiFragment : BoxContainer
+{
+ public Action? 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((string) AccountLineEdit.Text);
+ var pin = int.Parse((string) PinLineEdit.Text);
+ AccountLinkResultLabel.Visible = true;
+ _accountLinkActive = false;
+ OnLinkAttempt?.Invoke(new BankAccountLinkMessage(accountId, pin));
+ };
+ }
+
+ public void UpdateState(BankCartridgeUiState state)
+ {
+ var accountLinked = state.AccountId != null;
+
+ RichTextLabelExt.SetMarkup(AccountLinkMessageLabel, state.AccountLinkMessage);
+ RichTextLabelExt.SetMarkup(AccountLinkResultLabel, state.AccountLinkResult);
+
+ LinkedAccount.Visible = accountLinked;
+ NoLinkedAccountLabel.Visible = !accountLinked;
+
+ if (accountLinked)
+ {
+ RichTextLabelExt.SetMarkup(LinkedAccountNumberLabel, Loc.GetString("bank-program-ui-account-number-text",
+ ("account", state.AccountId!.Value)));
+ RichTextLabelExt.SetMarkup(LinkedAccountNameLabel, Loc.GetString("bank-program-ui-account-owner-text",
+ ("owner", state.OwnerName)));
+ RichTextLabelExt.SetMarkup(LinkedAccountBalanceLabel, Loc.GetString("atm-ui-balance", ("balance", state.Balance)));
+ UpdateAccountLinkUi();
+ return;
+ }
+
+ RichTextLabelExt.SetMarkup(NoLinkedAccountLabel, 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;
+ }
+}
diff --git a/Content.Client/ADT/Economy/UI/EftposBui.cs b/Content.Client/ADT/Economy/UI/EftposBui.cs
new file mode 100644
index 0000000000..a592eaef09
--- /dev/null
+++ b/Content.Client/ADT/Economy/UI/EftposBui.cs
@@ -0,0 +1,40 @@
+using JetBrains.Annotations;
+
+namespace Content.Client.ADT.Economy.UI;
+
+[UsedImplicitly]
+public sealed class EftposBui : BoundUserInterface
+{
+ private readonly EftposWindow _window;
+
+ public EftposBui(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+ _window = new EftposWindow();
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+ _window.OnClose += Close;
+ _window.OnCardButtonPressed += SendMessage;
+
+ if (State != null)
+ {
+ UpdateState(State);
+ }
+
+ _window.OpenCentered();
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+ _window.UpdateState(state);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ _window.Close();
+ base.Dispose(disposing);
+ }
+}
diff --git a/Content.Client/ADT/Economy/UI/EftposWindow.xaml b/Content.Client/ADT/Economy/UI/EftposWindow.xaml
new file mode 100644
index 0000000000..0cecebc71f
--- /dev/null
+++ b/Content.Client/ADT/Economy/UI/EftposWindow.xaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/ADT/Economy/UI/EftposWindow.xaml.cs b/Content.Client/ADT/Economy/UI/EftposWindow.xaml.cs
new file mode 100644
index 0000000000..392874cc7c
--- /dev/null
+++ b/Content.Client/ADT/Economy/UI/EftposWindow.xaml.cs
@@ -0,0 +1,58 @@
+using Content.Shared.ADT.Economy;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.ADT.Economy.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class EftposWindow : DefaultWindow
+{
+ public Action? OnCardButtonPressed;
+
+ public EftposWindow()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ AmountLineEdit.OnTextChanged += _ =>
+ {
+ if (!int.TryParse((string?)AmountLineEdit.Text, out var result) || result <= 0)
+ AmountLineEdit.Text = string.Empty;
+
+ AmountLineEdit.Text = result.ToString();
+ };
+
+ CardButton.OnPressed += _ =>
+ {
+ if (!int.TryParse((string?)AmountLineEdit.Text, out var result) || result < 0)
+ result = 0;
+
+ OnCardButtonPressed?.Invoke(new EftposLockMessage(result));
+ };
+ }
+
+ public void UpdateState(BoundUserInterfaceState state)
+ {
+ if (state is not EftposBuiState eftState)
+ return;
+
+ AmountLineEdit.Text = eftState.Amount == 0 ? string.Empty : eftState.Amount.ToString();
+ if (eftState.Locked)
+ {
+ CardButton.Text = Loc.GetString("eftpos-ui-card-unlock-text");
+ CardButton.ToolTip = Loc.GetString("eftpos-ui-card-unlock-desc");
+ AmountLineEdit.Editable = false;
+ }
+ else
+ {
+ CardButton.Text = Loc.GetString("eftpos-ui-card-lock-text");
+ CardButton.ToolTip = Loc.GetString("eftpos-ui-card-lock-desc");
+ AmountLineEdit.Editable = true;
+ }
+
+ AccountLabel.Text = eftState.Owner == string.Empty
+ ? string.Empty
+ : Loc.GetString("eftpos-ui-account-text", ("owner", eftState.Owner));
+ }
+}
diff --git a/Content.Client/CharacterInfo/CharacterInfoSystem.cs b/Content.Client/CharacterInfo/CharacterInfoSystem.cs
index aeaa48c6f2..a5f91017d7 100644
--- a/Content.Client/CharacterInfo/CharacterInfoSystem.cs
+++ b/Content.Client/CharacterInfo/CharacterInfoSystem.cs
@@ -32,7 +32,7 @@ public void RequestCharacterInfo()
private void OnCharacterInfoEvent(CharacterInfoEvent msg, EntitySessionEventArgs args)
{
var entity = GetEntity(msg.NetEntity);
- var data = new CharacterData(entity, msg.JobTitle, msg.Objectives, msg.Briefing, Name(entity));
+ var data = new CharacterData(entity, msg.JobTitle, msg.Objectives, msg.Briefing, Name(entity), msg.Memory); //ADT-Economy
OnCharacterUpdate?.Invoke(data);
}
@@ -49,7 +49,8 @@ public readonly record struct CharacterData(
string Job,
Dictionary> Objectives,
string? Briefing,
- string EntityName
+ string EntityName, //ADT-Economy-Start
+ Dictionary Memory //ADT-Economy-End
);
///
diff --git a/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs b/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs
index 1e4d2f2765..fce60dbc13 100644
--- a/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs
+++ b/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs
@@ -107,13 +107,14 @@ private void CharacterUpdated(CharacterData data)
return;
}
- var (entity, job, objectives, briefing, entityName) = data;
+ var (entity, job, objectives, briefing, entityName, memories) = data; //ADT-Economy
_window.SpriteView.SetEntity(entity);
_window.NameLabel.Text = entityName;
_window.SubText.Text = job;
_window.Objectives.RemoveAllChildren();
_window.ObjectivesLabel.Visible = objectives.Any();
+ _window.Memories.RemoveAllChildren(); //ADT-Economy
foreach (var (groupId, conditions) in objectives)
{
@@ -154,6 +155,26 @@ private void CharacterUpdated(CharacterData data)
_window.Objectives.AddChild(objectiveControl);
}
+ //ADT-Economy-Start
+ foreach (var (memoryName, memoryValue) in memories)
+ {
+ var memoryControl = new BoxContainer()
+ {
+ Orientation = BoxContainer.LayoutOrientation.Vertical,
+ Modulate = Color.Gray
+ };
+ var text = Loc.TryGetString(memoryName, out var t, ("value", memoryValue))
+ ? t
+ : $"{memoryName}: {memoryValue}";
+
+ memoryControl.AddChild(new Label
+ {
+ Text = text,
+ });
+ _window.Memories.AddChild(memoryControl);
+ }
+ //ADT-Economy-End
+
if (briefing != null)
{
var briefingControl = new ObjectiveBriefingControl();
diff --git a/Content.Client/UserInterface/Systems/Character/Windows/CharacterWindow.xaml b/Content.Client/UserInterface/Systems/Character/Windows/CharacterWindow.xaml
index c5307ab900..b28c5f89b6 100644
--- a/Content.Client/UserInterface/Systems/Character/Windows/CharacterWindow.xaml
+++ b/Content.Client/UserInterface/Systems/Character/Windows/CharacterWindow.xaml
@@ -14,6 +14,10 @@
+
+
+
+
diff --git a/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml b/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml
index 1fcb1a7898..44b1762e30 100644
--- a/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml
+++ b/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml
@@ -5,6 +5,13 @@
xmlns:style="clr-namespace:Content.Client.Stylesheets">
+
+
+
+
_dummies = [];
-
+ public Action? OnWithdraw; //ADT-Economy
public event Action? OnItemSelected;
public event Action? OnSearchChanged;
@@ -61,8 +61,18 @@ protected override void Dispose(bool disposing)
/// Populates the list of available items on the vending machine interface
/// and sets icons based on their prototypes
///
- public void Populate(List inventory, out List filteredInventory, string? filter = null)
+ public void Populate(List inventory, out List filteredInventory, double priceMultiplier, int credits, string? filter = null) //ADT-Economy
{
+ //ADT-Economy-Start
+ CreditsLabel.Text = Loc.GetString("vending-ui-credits-amount", ("credits", credits));
+ WithdrawButton.Disabled = credits == 0;
+ WithdrawButton.OnPressed += _ =>
+ {
+ if (credits == 0)
+ return;
+ OnWithdraw?.Invoke(new VendingMachineWithdrawMessage());
+ };
+ //ADT-Economy-End
filteredInventory = new();
if (inventory.Count == 0)
@@ -89,6 +99,7 @@ public void Populate(List inventory, out List
for (var i = 0; i < inventory.Count; i++)
{
var entry = inventory[i];
+ var price = (int)(entry.Price * priceMultiplier); //ADT-Economy
var vendingItem = VendingContents[i - filterCount];
vendingItem.Text = string.Empty;
vendingItem.Icon = null;
@@ -118,7 +129,7 @@ public void Populate(List inventory, out List
if (itemName.Length > longestEntry.Length)
longestEntry = itemName;
- vendingItem.Text = $"{itemName} [{entry.Amount}]";
+ vendingItem.Text = $" [{price}$] {itemName} [{entry.Amount}]"; //ADT-Economy
vendingItem.Icon = icon;
filteredInventory.Add(i);
}
diff --git a/Content.Client/VendingMachines/VendingMachineBoundUserInterface.cs b/Content.Client/VendingMachines/VendingMachineBoundUserInterface.cs
index eafab84ed6..a9903174d2 100644
--- a/Content.Client/VendingMachines/VendingMachineBoundUserInterface.cs
+++ b/Content.Client/VendingMachines/VendingMachineBoundUserInterface.cs
@@ -27,16 +27,19 @@ protected override void Open()
var vendingMachineSys = EntMan.System();
- _cachedInventory = vendingMachineSys.GetAllInventory(Owner);
+ var component = EntMan.GetComponent(Owner); //ADT-Economy
+ _cachedInventory = vendingMachineSys.GetAllInventory(Owner, component); //ADT-Economy
_menu = this.CreateWindow();
_menu.OpenCenteredLeft();
_menu.Title = EntMan.GetComponent(Owner).EntityName;
+ _menu.OnClose += Close; //ADT-Economy
_menu.OnItemSelected += OnItemSelected;
_menu.OnSearchChanged += OnSearchChanged;
+ _menu.OnWithdraw += SendMessage; //ADT-Economy
- _menu.Populate(_cachedInventory, out _cachedFilteredIndex);
+ _menu.Populate(_cachedInventory, out _cachedFilteredIndex, component.PriceMultiplier, component.Credits); //ADT-Economy
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -48,7 +51,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
_cachedInventory = newState.Inventory;
- _menu?.Populate(_cachedInventory, out _cachedFilteredIndex, _menu.SearchBar.Text);
+ _menu?.Populate(_cachedInventory, out _cachedFilteredIndex, newState.PriceMultiplier, newState.Credits); //ADT-Economy
}
private void OnItemSelected(ItemList.ItemListSelectedEventArgs args)
@@ -80,7 +83,10 @@ protected override void Dispose(bool disposing)
private void OnSearchChanged(string? filter)
{
- _menu?.Populate(_cachedInventory, out _cachedFilteredIndex, filter);
+ //ADT-Economy-Start
+ var component = EntMan.GetComponent(Owner);
+ _menu?.Populate(_cachedInventory, out _cachedFilteredIndex, component.PriceMultiplier, component.Credits, filter);
+ //ADT-Economy-End
}
}
}
diff --git a/Content.Server/ADT/Economy/ATMSystem.cs b/Content.Server/ADT/Economy/ATMSystem.cs
new file mode 100644
index 0000000000..558e914568
--- /dev/null
+++ b/Content.Server/ADT/Economy/ATMSystem.cs
@@ -0,0 +1,133 @@
+using System.Linq;
+using Content.Server.Stack;
+using Content.Server.Store.Components;
+using Content.Shared.Emag.Components;
+using Content.Shared.Emag.Systems;
+using Content.Shared.Interaction;
+using Content.Shared.Popups;
+using Content.Shared.Stacks;
+using Content.Shared.ADT.Economy;
+using Robust.Server.Containers;
+using Robust.Server.GameObjects;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Containers;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.ADT.Economy;
+
+public sealed class ATMSystem : SharedATMSystem
+{
+ [Dependency] private readonly UserInterfaceSystem _ui = default!;
+ [Dependency] private readonly BankCardSystem _bankCardSystem = default!;
+ [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
+ [Dependency] private readonly StackSystem _stackSystem = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+ [Dependency] private readonly ContainerSystem _container = default!;
+
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnCardInserted);
+ SubscribeLocalEvent(OnCardRemoved);
+ SubscribeLocalEvent(OnWithdrawRequest);
+ SubscribeLocalEvent(OnInteractUsing);
+ SubscribeLocalEvent(OnComponentStartup);
+ SubscribeLocalEvent(OnEmag);
+ }
+
+ private void OnEmag(EntityUid uid, ATMComponent component, ref GotEmaggedEvent args)
+ {
+ args.Handled = true;
+ }
+
+ private void OnComponentStartup(EntityUid uid, ATMComponent component, ComponentStartup args)
+ {
+ UpdateUiState(uid, -1, false, Loc.GetString("atm-ui-insert-card"));
+ }
+
+ private void OnInteractUsing(EntityUid uid, ATMComponent component, InteractUsingEvent args)
+ {
+ if (!TryComp(args.Used, out var currency) || !currency.Price.Keys.Contains(component.CurrencyType))
+ {
+ return;
+ }
+
+ if (!component.CardSlot.Item.HasValue)
+ {
+ _popupSystem.PopupEntity(Loc.GetString("atm-trying-insert-cash-error"), args.Target, args.User, PopupType.Medium);
+ _audioSystem.PlayPvs(component.SoundDeny, uid);
+ return;
+ }
+
+ var stack = Comp(args.Used);
+ var bankCard = Comp(component.CardSlot.Item.Value);
+ var amount = stack.Count;
+
+ _bankCardSystem.TryChangeBalance(bankCard.AccountId!.Value, amount);
+ Del(args.Used);
+
+ _audioSystem.PlayPvs(component.SoundInsertCurrency, uid);
+ 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(args.Entity, out var bankCard) || !bankCard.AccountId.HasValue)
+ {
+ _container.EmptyContainer(args.Container);
+ return;
+ }
+
+ UpdateUiState(uid, _bankCardSystem.GetBalance(bankCard.AccountId.Value), true, Loc.GetString("atm-ui-select-withdraw-amount"));
+ }
+
+ private void OnCardRemoved(EntityUid uid, ATMComponent component, EntRemovedFromContainerMessage args)
+ {
+ UpdateUiState(uid, -1, false, Loc.GetString("atm-ui-insert-card"));
+ }
+
+ private void OnWithdrawRequest(EntityUid uid, ATMComponent component, ATMRequestWithdrawMessage args)
+ {
+ if (!TryComp(component.CardSlot.Item, out var bankCard) || !bankCard.AccountId.HasValue)
+ {
+ if (component.CardSlot.ContainerSlot != null)
+ _container.EmptyContainer(component.CardSlot.ContainerSlot);
+ return;
+ }
+
+ if (!_bankCardSystem.TryGetAccount(bankCard.AccountId.Value, out var account) ||
+ account.AccountPin != args.Pin && !HasComp(uid))
+ {
+ _popupSystem.PopupEntity(Loc.GetString("atm-wrong-pin"), uid);
+ _audioSystem.PlayPvs(component.SoundDeny, uid);
+ return;
+ }
+
+ if (!_bankCardSystem.TryChangeBalance(account.AccountId, -args.Amount))
+ {
+ _popupSystem.PopupEntity(Loc.GetString("atm-not-enough-cash"), uid);
+ _audioSystem.PlayPvs(component.SoundDeny, uid);
+ return;
+ }
+
+ _stackSystem.Spawn(args.Amount, _prototypeManager.Index(component.CreditStackPrototype), Transform(uid).Coordinates);
+ _audioSystem.PlayPvs(component.SoundWithdrawCurrency, uid);
+
+ UpdateUiState(uid, account.Balance, true, Loc.GetString("atm-ui-select-withdraw-amount"));
+ }
+
+ private void UpdateUiState(EntityUid uid, int balance, bool hasCard, string infoMessage)
+ {
+ var state = new ATMBuiState
+ {
+ AccountBalance = balance,
+ HasCard = hasCard,
+ InfoMessage = infoMessage
+ };
+
+
+ _ui.SetUiState(uid, ATMUiKey.Key, state);
+ }
+}
diff --git a/Content.Server/ADT/Economy/BankAccount.cs b/Content.Server/ADT/Economy/BankAccount.cs
new file mode 100644
index 0000000000..87439a03c4
--- /dev/null
+++ b/Content.Server/ADT/Economy/BankAccount.cs
@@ -0,0 +1,23 @@
+using Content.Shared.Mind;
+
+namespace Content.Server.ADT.Economy;
+
+public sealed class BankAccount
+{
+ public readonly int AccountId;
+ public readonly int AccountPin;
+ public int Balance;
+ public bool CommandBudgetAccount;
+ public Entity? Mind;
+ public string Name = string.Empty;
+
+ public EntityUid? CartridgeUid;
+
+ public BankAccount(int accountId, int balance)
+ {
+ AccountId = accountId;
+ Balance = balance;
+ AccountPin = Random.Shared.Next(1000, 10000);
+ }
+}
+
diff --git a/Content.Server/ADT/Economy/BankCardSystem.cs b/Content.Server/ADT/Economy/BankCardSystem.cs
new file mode 100644
index 0000000000..e948251dd0
--- /dev/null
+++ b/Content.Server/ADT/Economy/BankCardSystem.cs
@@ -0,0 +1,226 @@
+using System.Diagnostics.CodeAnalysis;
+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;
+using Content.Server.Station.Systems;
+using Content.Shared.ADT.Economy;
+using Content.Shared.GameTicking;
+using Content.Shared.Inventory;
+using Content.Shared.Mind;
+using Content.Shared.Mind.Components;
+using Content.Shared.Mobs.Systems;
+using Robust.Server.Player;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Server.ADT.Economy;
+
+public sealed class BankCardSystem : EntitySystem
+{
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly IPrototypeManager _protoMan = default!;
+ [Dependency] private readonly IdCardSystem _idCardSystem = default!;
+ [Dependency] private readonly StationSystem _station = default!;
+ [Dependency] private readonly CargoSystem _cargo = default!;
+ [Dependency] private readonly ChatSystem _chatSystem = default!;
+ [Dependency] private readonly MobStateSystem _mobState = default!;
+ [Dependency] private readonly InventorySystem _inventorySystem = default!;
+ [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;
+
+ private SalaryPrototype _salaries = default!;
+ private readonly List _accounts = new();
+ private float _salaryTimer;
+
+ public override void Initialize()
+ {
+ _salaries = _protoMan.Index("Salaries");
+
+ SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnRoundRestart);
+ SubscribeLocalEvent(OnPlayerSpawned);
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ if (_gameTicker.RunLevel != GameRunLevel.InRound)
+ {
+ _salaryTimer = 0f;
+ return;
+ }
+
+ _salaryTimer += frameTime;
+
+ if (_salaryTimer <= SalaryDelay)
+ return;
+
+ _salaryTimer = 0f;
+ PaySalary();
+ }
+
+ private void PaySalary()
+ {
+ foreach (var account in _accounts.Where(account =>
+ account.Mind is {Comp.UserId: not null, Comp.CurrentEntity: not null} &&
+ _playerManager.TryGetSessionById(account.Mind.Value.Comp.UserId!.Value, out _) &&
+ !_mobState.IsDead(account.Mind.Value.Comp.CurrentEntity!.Value)))
+ {
+ account.Balance += GetSalary(account.Mind);
+ }
+
+ _chatSystem.DispatchGlobalAnnouncement(Loc.GetString("salary-pay-announcement"),
+ colorOverride: Color.FromHex("#18abf5"));
+ }
+
+ private int GetSalary(EntityUid? mind)
+ {
+ if (!_job.MindTryGetJob(mind, out _, out var job) || !_salaries.Salaries.TryGetValue(job.ID, out var salary))
+ return 0;
+
+ return salary;
+ }
+
+ private void OnStartup(EntityUid uid, BankCardComponent component, ComponentStartup args)
+ {
+ if (component.CommandBudgetCard &&
+ TryComp(_station.GetOwningStation(uid), out StationBankAccountComponent? acc))
+ {
+ component.AccountId = acc.BankAccount.AccountId;
+ return;
+ }
+ if (component.AccountId.HasValue)
+ {
+ CreateAccount(component.AccountId.Value, component.StartingBalance);
+ return;
+ }
+
+ var account = CreateAccount(default, component.StartingBalance);
+ component.AccountId = account.AccountId;
+ }
+
+ private void OnRoundRestart(RoundRestartCleanupEvent ev)
+ {
+ _accounts.Clear();
+ }
+
+ private void OnPlayerSpawned(PlayerSpawnCompleteEvent ev)
+ {
+ if (_idCardSystem.TryFindIdCard(ev.Mob, out var id) && TryComp(ev.Mob, out var mind))
+ {
+ var cardEntity = id.Owner;
+ var bankCardComponent = EnsureComp(cardEntity);
+
+ if (!bankCardComponent.AccountId.HasValue || !TryGetAccount(bankCardComponent.AccountId.Value, out var bankAccount))
+ return;
+
+ if (!TryComp(mind.Mind, out MindComponent? mindComponent))
+ return;
+
+ 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))
+ return;
+
+ BankCartridgeComponent? comp = null;
+
+ var programs = _cartridgeLoader.GetInstalled(pdaUid.Value);
+
+ var program = programs.ToList().Find(program => TryComp(program, out comp));
+ if (comp == null)
+ return;
+
+ bankAccount.CartridgeUid = program;
+ comp.AccountId = bankAccount.AccountId;
+ }
+ }
+
+ public BankAccount CreateAccount(int accountId = default, int startingBalance = 0)
+ {
+ if (TryGetAccount(accountId, out var acc))
+ return acc;
+
+ BankAccount account;
+ if (accountId == default)
+ {
+ int accountNumber;
+
+ do
+ {
+ accountNumber = _random.Next(100000, 999999);
+ } while (AccountExist(accountId));
+
+ account = new BankAccount(accountNumber, startingBalance);
+ }
+ else
+ {
+ account = new BankAccount(accountId, startingBalance);
+ }
+
+ _accounts.Add(account);
+
+ return account;
+ }
+
+ public bool AccountExist(int accountId)
+ {
+ return _accounts.Any(x => x.AccountId == accountId);
+ }
+
+ public bool TryGetAccount(int accountId, [NotNullWhen(true)] out BankAccount? account)
+ {
+ account = _accounts.FirstOrDefault(x => x.AccountId == accountId);
+ return account != null;
+ }
+
+ public int GetBalance(int accountId)
+ {
+ if (TryGetAccount(accountId, out var account))
+ {
+ return account.Balance;
+ }
+
+ return 0;
+ }
+
+ public bool TryChangeBalance(int accountId, int amount)
+ {
+ if (!TryGetAccount(accountId, out var account) || account.Balance + amount < 0)
+ return false;
+
+ if (account.CommandBudgetAccount)
+ {
+ while (AllEntityQuery().MoveNext(out var uid, out var acc))
+ {
+ if (acc.BankAccount.AccountId != accountId)
+ continue;
+
+ _cargo.UpdateBankAccount(uid, acc, amount);
+ return true;
+ }
+ }
+
+ account.Balance += amount;
+ if (account.CartridgeUid != null)
+ _bankCartridge.UpdateUiState(account.CartridgeUid.Value);
+
+ return true;
+ }
+}
+
diff --git a/Content.Server/ADT/Economy/BankCartridgeComponent.cs b/Content.Server/ADT/Economy/BankCartridgeComponent.cs
new file mode 100644
index 0000000000..c8bb5eef9f
--- /dev/null
+++ b/Content.Server/ADT/Economy/BankCartridgeComponent.cs
@@ -0,0 +1,13 @@
+namespace Content.Server.ADT.Economy;
+
+[RegisterComponent]
+public sealed partial class BankCartridgeComponent : Component
+{
+ [ViewVariables]
+ public int? AccountId;
+
+ [ViewVariables]
+ public EntityUid? Loader;
+
+ public string AccountLinkResult = string.Empty;
+}
diff --git a/Content.Server/ADT/Economy/BankCartridgeSystem.cs b/Content.Server/ADT/Economy/BankCartridgeSystem.cs
new file mode 100644
index 0000000000..16c7bf5dba
--- /dev/null
+++ b/Content.Server/ADT/Economy/BankCartridgeSystem.cs
@@ -0,0 +1,117 @@
+using Content.Server.CartridgeLoader;
+using Content.Shared.CartridgeLoader;
+using Content.Shared.ADT.Economy;
+using Content.Shared.PDA;
+
+namespace Content.Server.ADT.Economy;
+
+public sealed class BankCartridgeSystem : EntitySystem
+{
+ [Dependency] private readonly CartridgeLoaderSystem? _cartridgeLoaderSystem = default!;
+ [Dependency] private readonly BankCardSystem _bankCardSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnUiMessage);
+ SubscribeLocalEvent(OnUiReady);
+ SubscribeLocalEvent(OnInstall);
+ SubscribeLocalEvent(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(account.CartridgeUid.Value).AccountId = null;
+
+ account.CartridgeUid = uid;
+ component.AccountId = args.AccountId;
+ }
+
+ if (!TryComp(GetEntity(args.LoaderUid), out PdaComponent? pda) || !pda.ContainedId.HasValue ||
+ HasComp(pda.ContainedId.Value))
+ return;
+
+ var bankCard = AddComp(pda.ContainedId.Value);
+ bankCard.AccountId = account.AccountId;
+ }
+
+ private void OnUiReady(EntityUid uid, BankCartridgeComponent component, CartridgeUiReadyEvent args)
+ {
+ UpdateUiState(uid, args.Loader, component);
+ }
+
+ private void OnUiMessage(EntityUid uid, BankCartridgeComponent component, CartridgeMessageEvent args)
+ {
+ if (args is BankAccountLinkMessage message)
+ OnAccountLink(uid, component, message);
+
+ UpdateUiState(uid, GetEntity(args.LoaderUid), component);
+ }
+
+ 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
+ {
+ 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);
+ }
+}
diff --git a/Content.Server/ADT/Economy/CommandBudgetPinPaperComponent.cs b/Content.Server/ADT/Economy/CommandBudgetPinPaperComponent.cs
new file mode 100644
index 0000000000..e4c03b51ec
--- /dev/null
+++ b/Content.Server/ADT/Economy/CommandBudgetPinPaperComponent.cs
@@ -0,0 +1,6 @@
+namespace Content.Server.ADT.Economy;
+
+[RegisterComponent]
+public sealed partial class CommandBudgetPinPaperComponent : Component
+{
+}
diff --git a/Content.Server/ADT/Economy/CommandBudgetSystem.cs b/Content.Server/ADT/Economy/CommandBudgetSystem.cs
new file mode 100644
index 0000000000..6504702807
--- /dev/null
+++ b/Content.Server/ADT/Economy/CommandBudgetSystem.cs
@@ -0,0 +1,27 @@
+using Content.Server.Cargo.Components;
+using Content.Server.Paper;
+using Content.Server.Station.Systems;
+
+namespace Content.Server.ADT.Economy;
+
+public sealed class CommandBudgetSystem : EntitySystem
+{
+ [Dependency] private readonly PaperSystem _paper = default!;
+ [Dependency] private readonly StationSystem _station = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnMapInit);
+ }
+
+ private void OnMapInit(EntityUid uid, CommandBudgetPinPaperComponent component, MapInitEvent args)
+ {
+ if (!TryComp(_station.GetOwningStation(uid), out StationBankAccountComponent? account))
+ return;
+
+ var pin = account.BankAccount.AccountPin;
+ _paper.SetContent(uid,Loc. GetString("command-budget-pin-message", ("pin", pin)));
+ }
+}
diff --git a/Content.Server/ADT/Economy/EftposSystem.cs b/Content.Server/ADT/Economy/EftposSystem.cs
new file mode 100644
index 0000000000..f504b15ec4
--- /dev/null
+++ b/Content.Server/ADT/Economy/EftposSystem.cs
@@ -0,0 +1,88 @@
+using Content.Shared.ADT.Economy;
+using Content.Shared.Access.Components;
+using Content.Shared.Hands.Components;
+using Content.Shared.Interaction;
+using Content.Shared.Popups;
+using Robust.Server.GameObjects;
+using Robust.Shared.Audio.Systems;
+
+namespace Content.Server.ADT.Economy;
+
+public sealed class EftposSystem : EntitySystem
+{
+ [Dependency] private readonly UserInterfaceSystem _ui = default!;
+ [Dependency] private readonly BankCardSystem _bankCardSystem = default!;
+ [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+ [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnLock);
+ SubscribeLocalEvent(OnInteractUsing);
+ }
+
+ private void OnInteractUsing(EntityUid uid, EftposComponent component, InteractUsingEvent args)
+ {
+ if (component.BankAccountId == null || !TryComp(args.Used, out BankCardComponent? bankCard) ||
+ bankCard.AccountId == component.BankAccountId || component.Amount <= 0 || bankCard.CommandBudgetCard)
+ return;
+
+ if (_bankCardSystem.TryChangeBalance(bankCard.AccountId!.Value, -component.Amount) &&
+ _bankCardSystem.TryChangeBalance(component.BankAccountId.Value, component.Amount))
+ {
+ _popupSystem.PopupEntity(Loc.GetString("eftpos-transaction-success"), uid);
+ _audioSystem.PlayPvs(component.SoundApply, uid);
+ }
+ else
+ {
+ _popupSystem.PopupEntity(Loc.GetString("eftpos-transaction-error"), uid);
+ _audioSystem.PlayPvs(component.SoundDeny, uid);
+ }
+ }
+
+ private void OnLock(EntityUid uid, EftposComponent component, EftposLockMessage args)
+ {
+ if (!TryComp(args.Actor, out HandsComponent? hands) ||
+ !TryComp(hands.ActiveHandEntity, out BankCardComponent? bankCard))
+ return;
+
+ if (component.BankAccountId == null)
+ {
+ component.BankAccountId = bankCard.AccountId;
+ component.Amount = args.Amount;
+ }
+ else if (component.BankAccountId == bankCard.AccountId)
+ {
+ component.BankAccountId = null;
+ component.Amount = 0;
+ }
+
+ UpdateUiState(uid, component.BankAccountId != null, component.Amount,
+ GetOwner(hands.ActiveHandEntity.Value, component.BankAccountId));
+ }
+
+ private string GetOwner(EntityUid uid, int? bankAccountId)
+ {
+ if (bankAccountId == null || !_bankCardSystem.TryGetAccount(bankAccountId.Value, out var account))
+ return string.Empty;
+
+ if (TryComp(uid, out IdCardComponent? idCard) && idCard.FullName != null)
+ return idCard.FullName;
+
+ return account.Name == string.Empty ? account.AccountId.ToString() : account.Name;
+ }
+
+ private void UpdateUiState(EntityUid uid, bool locked, int amount, string owner)
+ {
+ var state = new EftposBuiState
+ {
+ Locked = locked,
+ Amount = amount,
+ Owner = owner
+ };
+
+ _ui.SetUiState(uid, EftposKey.Key, state);
+ }
+}
diff --git a/Content.Server/Cargo/Components/StationBankAccountComponent.cs b/Content.Server/Cargo/Components/StationBankAccountComponent.cs
index fe9be19b19..c0f8a0dcf4 100644
--- a/Content.Server/Cargo/Components/StationBankAccountComponent.cs
+++ b/Content.Server/Cargo/Components/StationBankAccountComponent.cs
@@ -1,3 +1,4 @@
+using Content.Server.ADT.Economy; //ADT-Economy
using Content.Shared.Cargo;
namespace Content.Server.Cargo.Components;
@@ -8,8 +9,17 @@ namespace Content.Server.Cargo.Components;
[RegisterComponent, Access(typeof(SharedCargoSystem))]
public sealed partial class StationBankAccountComponent : Component
{
- [ViewVariables(VVAccess.ReadWrite), DataField("balance")]
- public int Balance = 2000;
+ //ADT-Economy-Start
+ [ViewVariables(VVAccess.ReadWrite)]
+ public int Balance
+ {
+ get => BankAccount.Balance;
+ set => BankAccount.Balance = value;
+ }
+
+ [ViewVariables]
+ public BankAccount BankAccount = default!;
+ //ADT-Economy-End
///
/// How much the bank balance goes up per second, every Delay period. Rounded down when multiplied.
diff --git a/Content.Server/Cargo/Systems/CargoSystem.cs b/Content.Server/Cargo/Systems/CargoSystem.cs
index 1b33404e35..68ddc7a17f 100644
--- a/Content.Server/Cargo/Systems/CargoSystem.cs
+++ b/Content.Server/Cargo/Systems/CargoSystem.cs
@@ -1,6 +1,8 @@
+using Content.Server.ADT.Economy; //ADT-Economy
using Content.Server.Access.Systems;
using Content.Server.Cargo.Components;
using Content.Server.DeviceLinking.Systems;
+using Content.Server.GameTicking; //ADT-Economy
using Content.Server.Paper;
using Content.Server.Popups;
using Content.Server.Shuttles.Systems;
@@ -44,6 +46,9 @@ public sealed partial class CargoSystem : SharedCargoSystem
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
[Dependency] private readonly RadioSystem _radio = default!;
+ [Dependency] private readonly GameTicker _ticker = default!; //ADT-Economy
+ [Dependency] private readonly BankCardSystem _bankCard = default!; //ADT-Economy
+
private EntityQuery _xformQuery;
private EntityQuery _blacklistQuery;
@@ -67,7 +72,18 @@ public override void Initialize()
InitializeShuttle();
InitializeTelepad();
InitializeBounty();
+
+ SubscribeLocalEvent(OnInit); //ADT-Economy
+ }
+
+ //ADT-Economy-Start
+ private void OnInit(EntityUid uid, StationBankAccountComponent component, ComponentInit args)
+ {
+ component.BankAccount = _bankCard.CreateAccount(default, 2000);
+ component.BankAccount.CommandBudgetAccount = true;
+ component.BankAccount.Name = Loc.GetString("command-budget");
}
+ //ADT-Economy-End
public override void Update(float frameTime)
{
diff --git a/Content.Server/CharacterInfo/CharacterInfoSystem.cs b/Content.Server/CharacterInfo/CharacterInfoSystem.cs
index 3d83a66705..af35ef0ffb 100644
--- a/Content.Server/CharacterInfo/CharacterInfoSystem.cs
+++ b/Content.Server/CharacterInfo/CharacterInfoSystem.cs
@@ -32,6 +32,7 @@ private void OnRequestCharacterInfoEvent(RequestCharacterInfoEvent msg, EntitySe
var objectives = new Dictionary>();
var jobTitle = Loc.GetString("character-info-no-profession");
+ var memories = new Dictionary(); //ADT-Economy
string? briefing = null;
if (_minds.TryGetMind(entity, out var mindId, out var mind))
{
@@ -54,8 +55,15 @@ private void OnRequestCharacterInfoEvent(RequestCharacterInfoEvent msg, EntitySe
// Get briefing
briefing = _roles.MindGetBriefing(mindId);
+
+ //ADT-Economy-Start || Get memories
+ foreach (var memory in mind.AllMemories)
+ {
+ memories[memory.Name] = memory.Value;
+ }
+ //ADT-Economy-End
}
- RaiseNetworkEvent(new CharacterInfoEvent(GetNetEntity(entity), jobTitle, objectives, briefing), args.SenderSession);
+ RaiseNetworkEvent(new CharacterInfoEvent(GetNetEntity(entity), jobTitle, objectives, briefing, memories), args.SenderSession); //ADT-Economy
}
}
diff --git a/Content.Server/VendingMachines/VendingMachineSystem.cs b/Content.Server/VendingMachines/VendingMachineSystem.cs
index 2866b14a83..f7177d90e9 100644
--- a/Content.Server/VendingMachines/VendingMachineSystem.cs
+++ b/Content.Server/VendingMachines/VendingMachineSystem.cs
@@ -6,6 +6,9 @@
using Content.Server.Emp;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
+using Content.Server.Stack;
+using Content.Server.Store.Components;
+using Content.Server.ADT.Economy;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Actions;
@@ -15,11 +18,15 @@
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.Emp;
+using Content.Shared.Interaction;
+using Content.Shared.PDA;
using Content.Shared.Popups;
+using Content.Shared.Stacks;
+using Content.Shared.Tag;
using Content.Shared.Throwing;
using Content.Shared.UserInterface;
using Content.Shared.VendingMachines;
-using Content.Shared.Wall;
+using Content.Shared.ADT.Economy;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
@@ -39,8 +46,13 @@ public sealed class VendingMachineSystem : SharedVendingMachineSystem
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!;
+ //ADT-Economy-Start
+ [Dependency] private readonly BankCardSystem _bankCard = default!;
+ [Dependency] private readonly TagSystem _tag = default!;
+ [Dependency] private readonly StackSystem _stackSystem = default!;
+ //ADT-Economy-End
- private const float WallVendEjectDistanceFromWall = 1f;
+ private const double GlobalPriceMultiplier = 2.0; //ADT-Economy
public override void Initialize()
{
@@ -50,7 +62,7 @@ public override void Initialize()
SubscribeLocalEvent(OnPowerChanged);
SubscribeLocalEvent(OnBreak);
SubscribeLocalEvent(OnEmagged);
- SubscribeLocalEvent(OnDamageChanged);
+ SubscribeLocalEvent(OnDamage); //ADT-Economy
SubscribeLocalEvent(OnVendingPrice);
SubscribeLocalEvent(OnEmpPulse);
@@ -66,6 +78,11 @@ public override void Initialize()
SubscribeLocalEvent(OnDoAfter);
+ //ADT-Economy-Start
+ SubscribeLocalEvent(OnInteractUsing);
+ SubscribeLocalEvent(OnWithdrawMessage);
+ //ADT-Economy-End
+
SubscribeLocalEvent(OnPriceCalculation);
}
@@ -116,7 +133,8 @@ private void OnBoundUIOpened(EntityUid uid, VendingMachineComponent component, B
private void UpdateVendingMachineInterfaceState(EntityUid uid, VendingMachineComponent component)
{
- var state = new VendingMachineInterfaceState(GetAllInventory(uid, component));
+ var state = new VendingMachineInterfaceState(GetAllInventory(uid, component), GetPriceMultiplier(component),
+ component.Credits); //ADT-Economy
_userInterfaceSystem.SetUiState(uid, VendingMachineUiKey.Key, state);
}
@@ -145,19 +163,14 @@ private void OnBreak(EntityUid uid, VendingMachineComponent vendComponent, Break
private void OnEmagged(EntityUid uid, VendingMachineComponent component, ref GotEmaggedEvent args)
{
- // only emag if there are emag-only items
- args.Handled = component.EmaggedInventory.Count > 0;
+ //ADT-Economy-Start
+ args.Handled = component.EmaggedInventory.Count > 0 || component.PriceMultiplier > 0;
+ UpdateVendingMachineInterfaceState(uid, component);
+ //ADT-Economy-End
}
- private void OnDamageChanged(EntityUid uid, VendingMachineComponent component, DamageChangedEvent args)
+ private void OnDamage(EntityUid uid, VendingMachineComponent component, DamageChangedEvent args) //ADT-Economy
{
- if (!args.DamageIncreased && component.Broken)
- {
- component.Broken = false;
- TryUpdateVisualState(uid, component);
- return;
- }
-
if (component.Broken || component.DispenseOnHitCoolingDown ||
component.DispenseOnHitChance == null || args.DamageDelta == null)
return;
@@ -202,6 +215,54 @@ private void OnDoAfter(EntityUid uid, VendingMachineComponent component, DoAfter
args.Handled = true;
}
+ //ADT-Economy-Start
+ private void OnInteractUsing(EntityUid uid, VendingMachineComponent component, InteractUsingEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ if (component.Broken || !this.IsPowered(uid, EntityManager))
+ return;
+
+ if (!TryComp(args.Used, out var currency) ||
+ !currency.Price.Keys.Contains(component.CurrencyType))
+ return;
+
+ var stack = Comp(args.Used);
+ component.Credits += stack.Count;
+ Del(args.Used);
+ UpdateVendingMachineInterfaceState(uid, component);
+ Audio.PlayPvs(component.SoundInsertCurrency, uid);
+ args.Handled = true;
+ }
+
+ protected override int GetEntryPrice(EntityPrototype proto)
+ {
+ var price = (int) _pricing.GetEstimatedPrice(proto);
+ return price > 0 ? price : 25;
+ }
+
+ private int GetPrice(VendingMachineInventoryEntry entry, VendingMachineComponent comp)
+ {
+ return (int) (entry.Price * GetPriceMultiplier(comp));
+ }
+
+ private double GetPriceMultiplier(VendingMachineComponent comp)
+ {
+ return comp.PriceMultiplier * GlobalPriceMultiplier;
+ }
+
+ private void OnWithdrawMessage(EntityUid uid, VendingMachineComponent component, VendingMachineWithdrawMessage args)
+ {
+ _stackSystem.Spawn(component.Credits, PrototypeManager.Index(component.CreditStackPrototype),
+ Transform(uid).Coordinates);
+ component.Credits = 0;
+ Audio.PlayPvs(component.SoundWithdrawCurrency, uid);
+
+ UpdateVendingMachineInterfaceState(uid, component);
+ }
+ //ADT-Economy-End
+
///
/// Sets the property of the vending machine.
///
@@ -243,7 +304,7 @@ public bool IsAuthorized(EntityUid uid, EntityUid sender, VendingMachineComponen
if (_accessReader.IsAllowed(sender, uid, accessReader) || HasComp(uid))
return true;
- Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid);
+ Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid, sender); //ADT-Economy
Deny(uid, vendComponent);
return false;
}
@@ -257,7 +318,7 @@ public bool IsAuthorized(EntityUid uid, EntityUid sender, VendingMachineComponen
/// The prototype ID of the item
/// Whether the item should be thrown in a random direction after ejection
///
- public void TryEjectVendorItem(EntityUid uid, InventoryType type, string itemId, bool throwItem, VendingMachineComponent? vendComponent = null)
+ public void TryEjectVendorItem(EntityUid uid, InventoryType type, string itemId, bool throwItem, VendingMachineComponent? vendComponent = null, EntityUid? sender = null) //ADT-Economy
{
if (!Resolve(uid, ref vendComponent))
return;
@@ -271,14 +332,22 @@ public void TryEjectVendorItem(EntityUid uid, InventoryType type, string itemId,
if (entry == null)
{
- Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-invalid-item"), uid);
+ //ADT-Economy-Start
+ if (sender.HasValue)
+ Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-invalid-item"), uid, sender.Value);
+ //ADT-Economy-End
+
Deny(uid, vendComponent);
return;
}
if (entry.Amount <= 0)
{
- Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-out-of-stock"), uid);
+ //ADT-Economy-Start
+ if (sender.HasValue)
+ Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-out-of-stock"), uid, sender.Value);
+ //ADT-Economy-End
+
Deny(uid, vendComponent);
return;
}
@@ -286,6 +355,44 @@ public void TryEjectVendorItem(EntityUid uid, InventoryType type, string itemId,
if (string.IsNullOrEmpty(entry.ID))
return;
+ //ADT-Economy-Start
+ var price = GetPrice(entry, vendComponent);
+ if (price > 0 && sender.HasValue && !_tag.HasTag(sender.Value, "IgnoreBalanceChecks"))
+ {
+ var success = false;
+ if (vendComponent.Credits >= price)
+ {
+ vendComponent.Credits -= price;
+ success = true;
+ }
+ else
+ {
+ var items = _accessReader.FindPotentialAccessItems(sender.Value);
+ foreach (var item in items)
+ {
+ var nextItem = item;
+ if (TryComp(item, out PdaComponent? pda) && pda.ContainedId is { Valid: true } id)
+ nextItem = id;
+
+ if (!TryComp(nextItem, out var bankCard) || !bankCard.AccountId.HasValue
+ || !_bankCard.TryGetAccount(bankCard.AccountId.Value, out var account)
+ || account.Balance < price)
+ continue;
+
+ _bankCard.TryChangeBalance(bankCard.AccountId.Value, -price);
+ success = true;
+ break;
+ }
+ }
+
+ if (!success)
+ {
+ Popup.PopupEntity(Loc.GetString("vending-machine-component-no-balance"), uid);
+ Deny(uid, vendComponent);
+ return;
+ }
+ }
+ //ADT-Economy-End
// Start Ejecting, and prevent users from ordering while anim playing
vendComponent.Ejecting = true;
@@ -313,7 +420,7 @@ public void AuthorizedVend(EntityUid uid, EntityUid sender, InventoryType type,
{
if (IsAuthorized(uid, sender, component))
{
- TryEjectVendorItem(uid, type, itemId, component.CanShoot, component);
+ TryEjectVendorItem(uid, type, itemId, component.CanShoot, component, sender); //ADT-Economy-Start
}
}
@@ -394,20 +501,7 @@ private void EjectItem(EntityUid uid, VendingMachineComponent? vendComponent = n
return;
}
- // Default spawn coordinates
- var spawnCoordinates = Transform(uid).Coordinates;
-
- //Make sure the wallvends spawn outside of the wall.
-
- if (TryComp(uid, out var wallMountComponent))
- {
-
- var offset = wallMountComponent.Direction.ToWorldVec() * WallVendEjectDistanceFromWall;
- spawnCoordinates = spawnCoordinates.Offset(offset);
- }
-
- var ent = Spawn(vendComponent.NextItemToEject, spawnCoordinates);
-
+ var ent = Spawn(vendComponent.NextItemToEject, Transform(uid).Coordinates); //ADT-Economy-Start
if (vendComponent.ThrowNextItem)
{
var range = vendComponent.NonLimitedEjectRange;
diff --git a/Content.Shared/ADT/Economy/ATMComponent.cs b/Content.Shared/ADT/Economy/ATMComponent.cs
new file mode 100644
index 0000000000..df23bab583
--- /dev/null
+++ b/Content.Shared/ADT/Economy/ATMComponent.cs
@@ -0,0 +1,40 @@
+using Content.Shared.Containers.ItemSlots;
+using Content.Shared.Stacks;
+using Robust.Shared.Audio;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.ADT.Economy;
+
+[RegisterComponent]
+public sealed partial class ATMComponent : Component
+{
+ [DataField("idCardSlot")]
+ public ItemSlot CardSlot = new ();
+
+ [DataField("currencyType")]
+ public string CurrencyType = "SpaceCash";
+
+ public string SlotId = "card-slot";
+
+ [ValidatePrototypeId]
+ public string CreditStackPrototype = "Credit";
+
+ [DataField("soundInsertCurrency")]
+ public SoundSpecifier SoundInsertCurrency = new SoundPathSpecifier("/Audio/ADT/Machines/polaroid2.ogg");
+
+ [DataField("soundWithdrawCurrency")]
+ public SoundSpecifier SoundWithdrawCurrency = new SoundPathSpecifier("/Audio/ADT/Machines/polaroid1.ogg");
+
+ [DataField("soundApply")]
+ public SoundSpecifier SoundApply = new SoundPathSpecifier("/Audio/ADT/Machines/chime.ogg");
+
+ [DataField("soundDeny")]
+ public SoundSpecifier SoundDeny = new SoundPathSpecifier("/Audio/ADT/Machines/buzz-sigh.ogg");
+}
+
+
+[Serializable, NetSerializable]
+public enum ATMUiKey
+{
+ Key
+}
diff --git a/Content.Shared/ADT/Economy/ATMMessages.cs b/Content.Shared/ADT/Economy/ATMMessages.cs
new file mode 100644
index 0000000000..b8707b9d86
--- /dev/null
+++ b/Content.Shared/ADT/Economy/ATMMessages.cs
@@ -0,0 +1,24 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.ADT.Economy;
+
+[Serializable, NetSerializable]
+public sealed class ATMRequestWithdrawMessage : BoundUserInterfaceMessage
+{
+ public int Amount;
+ public int Pin;
+
+ public ATMRequestWithdrawMessage(int amount, int pin)
+ {
+ Amount = amount;
+ Pin = pin;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class ATMBuiState : BoundUserInterfaceState
+{
+ public bool HasCard;
+ public string InfoMessage = string.Empty;
+ public int AccountBalance;
+}
diff --git a/Content.Shared/ADT/Economy/BankCardComponent.cs b/Content.Shared/ADT/Economy/BankCardComponent.cs
new file mode 100644
index 0000000000..adacc57708
--- /dev/null
+++ b/Content.Shared/ADT/Economy/BankCardComponent.cs
@@ -0,0 +1,16 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.ADT.Economy;
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class BankCardComponent : Component
+{
+ [DataField]
+ public int? AccountId;
+
+ [DataField]
+ public int StartingBalance;
+
+ [DataField]
+ public bool CommandBudgetCard;
+}
diff --git a/Content.Shared/ADT/Economy/BankCartridgeUiState.cs b/Content.Shared/ADT/Economy/BankCartridgeUiState.cs
new file mode 100644
index 0000000000..1de49a717f
--- /dev/null
+++ b/Content.Shared/ADT/Economy/BankCartridgeUiState.cs
@@ -0,0 +1,27 @@
+using Content.Shared.CartridgeLoader;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.ADT.Economy;
+
+[Serializable, NetSerializable]
+public sealed class BankCartridgeUiState : BoundUserInterfaceState
+{
+ public int Balance;
+ public int? AccountId = null;
+ public string OwnerName = string.Empty;
+ 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;
+ }
+}
diff --git a/Content.Shared/ADT/Economy/EftposComponent.cs b/Content.Shared/ADT/Economy/EftposComponent.cs
new file mode 100644
index 0000000000..8298a447f9
--- /dev/null
+++ b/Content.Shared/ADT/Economy/EftposComponent.cs
@@ -0,0 +1,26 @@
+using Robust.Shared.Audio;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.ADT.Economy;
+
+[RegisterComponent]
+public sealed partial class EftposComponent : Component
+{
+ [ViewVariables]
+ public int? BankAccountId;
+
+ [ViewVariables]
+ public int Amount;
+
+ [DataField("soundApply")]
+ public SoundSpecifier SoundApply = new SoundPathSpecifier("/Audio/ADT/Machines/chime.ogg");
+
+ [DataField("soundDeny")]
+ public SoundSpecifier SoundDeny = new SoundPathSpecifier("/Audio/ADT/Machines/buzz-sigh.ogg");
+}
+
+[Serializable, NetSerializable]
+public enum EftposKey
+{
+ Key
+}
diff --git a/Content.Shared/ADT/Economy/EftposMessages.cs b/Content.Shared/ADT/Economy/EftposMessages.cs
new file mode 100644
index 0000000000..33ee90dba5
--- /dev/null
+++ b/Content.Shared/ADT/Economy/EftposMessages.cs
@@ -0,0 +1,22 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.ADT.Economy;
+
+[Serializable, NetSerializable]
+public sealed class EftposBuiState : BoundUserInterfaceState
+{
+ public bool Locked;
+ public int Amount;
+ public string Owner = string.Empty;
+}
+
+[Serializable, NetSerializable]
+public sealed class EftposLockMessage : BoundUserInterfaceMessage
+{
+ public int Amount;
+
+ public EftposLockMessage(int amount)
+ {
+ Amount = amount;
+ }
+}
diff --git a/Content.Shared/ADT/Economy/SalaryPrototype.cs b/Content.Shared/ADT/Economy/SalaryPrototype.cs
new file mode 100644
index 0000000000..6d4166db03
--- /dev/null
+++ b/Content.Shared/ADT/Economy/SalaryPrototype.cs
@@ -0,0 +1,13 @@
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.ADT.Economy;
+
+[Prototype("salary")]
+public sealed class SalaryPrototype : IPrototype
+{
+ [IdDataField]
+ public string ID { get; } = default!;
+
+ [DataField("salaries")]
+ public Dictionary Salaries = new();
+}
diff --git a/Content.Shared/ADT/Economy/SharedATMSystem.cs b/Content.Shared/ADT/Economy/SharedATMSystem.cs
new file mode 100644
index 0000000000..4d14c7baa8
--- /dev/null
+++ b/Content.Shared/ADT/Economy/SharedATMSystem.cs
@@ -0,0 +1,26 @@
+using Content.Shared.Containers.ItemSlots;
+
+namespace Content.Shared.ADT.Economy;
+
+public abstract class SharedATMSystem : EntitySystem
+{
+ [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnATMInit);
+ SubscribeLocalEvent(OnATMRemoved);
+ }
+
+ protected virtual void OnATMInit(EntityUid uid, ATMComponent component, ComponentInit args)
+ {
+ _itemSlotsSystem.AddItemSlot(uid, component.SlotId, component.CardSlot);
+ }
+
+ private void OnATMRemoved(EntityUid uid, ATMComponent component, ComponentRemove args)
+ {
+ _itemSlotsSystem.TryEject(uid, component.CardSlot, null!, out _);
+ _itemSlotsSystem.RemoveItemSlot(uid, component.CardSlot);
+ }
+}
diff --git a/Content.Shared/CharacterInfo/SharedCharacterInfoSystem.cs b/Content.Shared/CharacterInfo/SharedCharacterInfoSystem.cs
index 550a81313a..5f312e1b45 100644
--- a/Content.Shared/CharacterInfo/SharedCharacterInfoSystem.cs
+++ b/Content.Shared/CharacterInfo/SharedCharacterInfoSystem.cs
@@ -21,12 +21,14 @@ public sealed class CharacterInfoEvent : EntityEventArgs
public readonly string JobTitle;
public readonly Dictionary> Objectives;
public readonly string? Briefing;
+ public readonly Dictionary Memory; //ADT-Economy
- public CharacterInfoEvent(NetEntity netEntity, string jobTitle, Dictionary> objectives, string? briefing)
+ public CharacterInfoEvent(NetEntity netEntity, string jobTitle, Dictionary> objectives, string? briefing, Dictionary memory) //ADT-Economy
{
NetEntity = netEntity;
JobTitle = jobTitle;
Objectives = objectives;
Briefing = briefing;
+ Memory = memory; //ADT-Economy
}
}
diff --git a/Content.Shared/Mind/Memory.cs b/Content.Shared/Mind/Memory.cs
new file mode 100644
index 0000000000..60937cb332
--- /dev/null
+++ b/Content.Shared/Mind/Memory.cs
@@ -0,0 +1,16 @@
+namespace Content.Shared.Mind;
+
+public sealed class Memory
+{
+ [ViewVariables(VVAccess.ReadWrite)]
+ public string Name { get; set; }
+
+ [ViewVariables(VVAccess.ReadWrite)]
+ public string Value { get; set; }
+
+ public Memory(string name, string value)
+ {
+ Name = name;
+ Value = value;
+ }
+}
diff --git a/Content.Shared/Mind/MindComponent.cs b/Content.Shared/Mind/MindComponent.cs
index 51a174c846..b4a8439052 100644
--- a/Content.Shared/Mind/MindComponent.cs
+++ b/Content.Shared/Mind/MindComponent.cs
@@ -29,6 +29,8 @@ public sealed partial class MindComponent : Component
[DataField, AutoNetworkedField]
public List Objectives = new();
+ internal readonly HashSet Memories = new(); //ADT-Economy
+
///
/// The session ID of the player owning this mind.
///
@@ -63,6 +65,21 @@ public sealed partial class MindComponent : Component
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
public string? CharacterName { get; set; }
+
+ //ADT-Economy-Start
+ [ViewVariables]
+ public IEnumerable AllMemories => Memories;
+
+ public void AddMemory(Memory memory)
+ {
+ if (Memories.Contains(memory))
+ {
+ return;
+ }
+
+ Memories.Add(memory);
+ }
+ //ADT-Economy-End
///
/// The time of death for this Mind.
diff --git a/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs b/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs
index 59f8489ac6..be86a01660 100644
--- a/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs
+++ b/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs
@@ -77,7 +77,7 @@ public List GetAvailableInventory(EntityUid uid, V
if (!Resolve(uid, ref component))
return new();
- return GetAllInventory(uid, component).Where(_ => _.Amount > 0).ToList();
+ return GetAllInventory(uid, component).Where(inventoryEntry => inventoryEntry.Amount > 0).ToList(); //ADT-Economy
}
private void AddInventoryFromPrototype(EntityUid uid, Dictionary? entries,
@@ -107,7 +107,7 @@ private void AddInventoryFromPrototype(EntityUid uid, Dictionary?
foreach (var (id, amount) in entries)
{
- if (PrototypeManager.HasIndex(id))
+ if (PrototypeManager.TryIndex(id, out var proto)) //ADT-Economy
{
var restock = amount;
var chanceOfMissingStock = 1 - restockQuality;
@@ -125,10 +125,23 @@ private void AddInventoryFromPrototype(EntityUid uid, Dictionary?
// restocking a machine who doesn't want to force vend out
// all the items just to restock one empty slot without
// losing the rest of the restock.
- entry.Amount = Math.Min(entry.Amount + amount, 3 * restock);
+
+ //ADT-Economy-Start
+ entry.Amount = Math.Min(entry.Amount + amount, 3 * amount);
else
- inventory.Add(id, new VendingMachineInventoryEntry(type, id, restock));
+ {
+ var price = GetEntryPrice(proto);
+ inventory.Add(id, new VendingMachineInventoryEntry(type, id, amount, price));
+ }
+ //ADT-Economy-End
}
}
}
+
+ //ADT-Economy-Stat
+ protected virtual int GetEntryPrice(EntityPrototype proto)
+ {
+ return 25;
+ }
+ //ADT-Economy-End
}
diff --git a/Content.Shared/VendingMachines/VendingMachineComponent.cs b/Content.Shared/VendingMachines/VendingMachineComponent.cs
index 23130bb8f3..1f4ffcc42a 100644
--- a/Content.Shared/VendingMachines/VendingMachineComponent.cs
+++ b/Content.Shared/VendingMachines/VendingMachineComponent.cs
@@ -1,4 +1,5 @@
using Content.Shared.Actions;
+using Content.Shared.Stacks;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
@@ -187,6 +188,27 @@ public sealed partial class VendingMachineComponent : Component
[DataField("loopDeny")]
public bool LoopDenyAnimation = true;
#endregion
+
+ //ADT-Economy-Start
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public double PriceMultiplier = 0.25;
+
+ public ProtoId CreditStackPrototype = "Credit";
+
+ [DataField]
+ public string CurrencyType = "SpaceCash";
+
+ [DataField]
+ public SoundSpecifier SoundInsertCurrency =
+ new SoundPathSpecifier("/Audio/ADT/Machines/polaroid2.ogg");
+
+ [DataField]
+ public SoundSpecifier SoundWithdrawCurrency =
+ new SoundPathSpecifier("/Audio/ADT/Machines/polaroid1.ogg");
+
+ [ViewVariables]
+ public int Credits;
+ //ADT-Economy-End
}
[Serializable, NetSerializable]
@@ -198,11 +220,15 @@ public sealed class VendingMachineInventoryEntry
public string ID;
[ViewVariables(VVAccess.ReadWrite)]
public uint Amount;
- public VendingMachineInventoryEntry(InventoryType type, string id, uint amount)
+ [ViewVariables(VVAccess.ReadWrite)] //ADT-Economy
+ public int Price; //ADT-Economy
+
+ public VendingMachineInventoryEntry(InventoryType type, string id, uint amount, int price)
{
Type = type;
ID = id;
Amount = amount;
+ Price = price; //ADT-Economy
}
}
diff --git a/Content.Shared/VendingMachines/VendingMachineInterfaceState.cs b/Content.Shared/VendingMachines/VendingMachineInterfaceState.cs
index 82758b17f6..121047205b 100644
--- a/Content.Shared/VendingMachines/VendingMachineInterfaceState.cs
+++ b/Content.Shared/VendingMachines/VendingMachineInterfaceState.cs
@@ -6,12 +6,26 @@ namespace Content.Shared.VendingMachines
public sealed class VendingMachineInterfaceState : BoundUserInterfaceState
{
public List Inventory;
-
- public VendingMachineInterfaceState(List inventory)
+ //ADT-Economy-Start
+ public double PriceMultiplier;
+ public int Credits;
+ public VendingMachineInterfaceState(List inventory, double priceMultiplier,
+ int credits)
+ //ADT-Economy-End
{
Inventory = inventory;
+ //ADT-Economy-Start
+ PriceMultiplier = priceMultiplier;
+ Credits = credits;
+ //ADT-Economy-End
}
}
+ //ADT-Economy-Start
+ [Serializable, NetSerializable]
+ public sealed class VendingMachineWithdrawMessage : BoundUserInterfaceMessage
+ {
+ }
+ //ADT-Economy-End
[Serializable, NetSerializable]
public sealed class VendingMachineEjectMessage : BoundUserInterfaceMessage
diff --git a/Resources/Audio/ADT/Machines/buzz-sigh.ogg b/Resources/Audio/ADT/Machines/buzz-sigh.ogg
new file mode 100644
index 0000000000..109c196e2c
Binary files /dev/null and b/Resources/Audio/ADT/Machines/buzz-sigh.ogg differ
diff --git a/Resources/Audio/ADT/Machines/buzz-two.ogg b/Resources/Audio/ADT/Machines/buzz-two.ogg
new file mode 100644
index 0000000000..3f79e2a0e9
Binary files /dev/null and b/Resources/Audio/ADT/Machines/buzz-two.ogg differ
diff --git a/Resources/Audio/ADT/Machines/chime.ogg b/Resources/Audio/ADT/Machines/chime.ogg
new file mode 100644
index 0000000000..f20eb11d11
Binary files /dev/null and b/Resources/Audio/ADT/Machines/chime.ogg differ
diff --git a/Resources/Audio/ADT/Machines/license.txt b/Resources/Audio/ADT/Machines/license.txt
new file mode 100644
index 0000000000..6197385277
--- /dev/null
+++ b/Resources/Audio/ADT/Machines/license.txt
@@ -0,0 +1,6 @@
+buzz-sigh.ogg taken from: https://github.com/Baystation12/Baystation12 at commit 662c08272acd7be79531550919f56f846726eabb
+buzz-two.ogg taken from: https://github.com/Baystation12/Baystation12 at commit 662c08272acd7be79531550919f56f846726eabb
+chime.ogg taken from: https://github.com/Baystation12/Baystation12 at commit 662c08272acd7be79531550919f56f846726eabb
+polaroid1.ogg taken from: https://github.com/Baystation12/Baystation12 at commit 662c08272acd7be79531550919f56f846726eabb and edited (stereo to mono)
+polaroid2.ogg taken from: https://github.com/Baystation12/Baystation12 at commit 662c08272acd7be79531550919f56f846726eabb and edited (stereo to mono)
+twobeep.ogg taken from: https://github.com/Baystation12/Baystation12 at commit 662c08272acd7be79531550919f56f846726eabb
\ No newline at end of file
diff --git a/Resources/Audio/ADT/Machines/polaroid1.ogg b/Resources/Audio/ADT/Machines/polaroid1.ogg
new file mode 100644
index 0000000000..bd2a641e64
Binary files /dev/null and b/Resources/Audio/ADT/Machines/polaroid1.ogg differ
diff --git a/Resources/Audio/ADT/Machines/polaroid2.ogg b/Resources/Audio/ADT/Machines/polaroid2.ogg
new file mode 100644
index 0000000000..0c903d45ba
Binary files /dev/null and b/Resources/Audio/ADT/Machines/polaroid2.ogg differ
diff --git a/Resources/Audio/ADT/Machines/twobeep.ogg b/Resources/Audio/ADT/Machines/twobeep.ogg
new file mode 100644
index 0000000000..26fc5a95a5
Binary files /dev/null and b/Resources/Audio/ADT/Machines/twobeep.ogg differ
diff --git a/Resources/Locale/ru-RU/ADT/economy/announcemets.ftl b/Resources/Locale/ru-RU/ADT/economy/announcemets.ftl
new file mode 100644
index 0000000000..d22f9ba8e0
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/economy/announcemets.ftl
@@ -0,0 +1 @@
+salary-pay-announcement = Внимание! Наши автоматизированные системы передают сигнал, полученный от Центрального командования. Согласно нашему трудовому догову, экипажу станции была выплачена заработная плата.
diff --git a/Resources/Locale/ru-RU/ADT/economy/atm.ftl b/Resources/Locale/ru-RU/ADT/economy/atm.ftl
new file mode 100644
index 0000000000..a1e20b13f8
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/economy/atm.ftl
@@ -0,0 +1,7 @@
+atm-trying-insert-cash-error = Купюроприемник закрыт.
+atm-wrong-pin = Неверный PIN-код.
+atm-not-enough-cash = Недостаточно средств.
+atm-ui-enter-pin = Введите PIN
+atm-ui-select-withdraw-amount = Выберите сумму вывода.
+atm-ui-insert-card = Вставьте карту.
+atm-ui-balance = Баланс: { $balance }$
diff --git a/Resources/Locale/ru-RU/ADT/economy/bank.ftl b/Resources/Locale/ru-RU/ADT/economy/bank.ftl
new file mode 100644
index 0000000000..072523cc8e
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/economy/bank.ftl
@@ -0,0 +1,14 @@
+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]
diff --git a/Resources/Locale/ru-RU/ADT/economy/command.ftl b/Resources/Locale/ru-RU/ADT/economy/command.ftl
new file mode 100644
index 0000000000..560565a64d
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/economy/command.ftl
@@ -0,0 +1,2 @@
+command-budget = Бюджет командования
+command-budget-pin-message = PIN-код станционного бюджета: { $pin }. Постарайтесь сохранить его в тайне до прилёта на станцию центрального командования.
diff --git a/Resources/Locale/ru-RU/ADT/economy/currency.ftl b/Resources/Locale/ru-RU/ADT/economy/currency.ftl
new file mode 100644
index 0000000000..1bc38c58a3
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/economy/currency.ftl
@@ -0,0 +1,6 @@
+store-currency-display-space-cash =
+ { $amount ->
+ [one] { $amount } евродоллар
+ [few] { $amount } евродоллара
+ *[other] { $amount } евродолларов
+ }
diff --git a/Resources/Locale/ru-RU/ADT/economy/eftpos.ftl b/Resources/Locale/ru-RU/ADT/economy/eftpos.ftl
new file mode 100644
index 0000000000..8f9309f763
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/economy/eftpos.ftl
@@ -0,0 +1,9 @@
+eftpos-ui-title = Терминал безналичной оплаты
+eftpos-ui-amount-text = Сумма кредитов:
+eftpos-ui-card-lock-text = Заблокировать
+eftpos-ui-card-unlock-text = Разблокировать
+eftpos-ui-card-lock-desc = Проведите картой для блокировки
+eftpos-ui-card-unlock-desc = Проведите картой для разблокировки
+eftpos-ui-account-text = Аккаунт: { $owner }
+eftpos-transaction-success = Транзакция успешно завершена
+eftpos-transaction-error = Ошибка транзакции
diff --git a/Resources/Locale/ru-RU/ADT/economy/memory.ftl b/Resources/Locale/ru-RU/ADT/economy/memory.ftl
new file mode 100644
index 0000000000..84aeea339b
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/economy/memory.ftl
@@ -0,0 +1,2 @@
+character-info-memories-label = Память
+character-info-memories-account-number = Аккаунт №
diff --git a/Resources/Locale/ru-RU/ADT/economy/vending.ftl b/Resources/Locale/ru-RU/ADT/economy/vending.ftl
new file mode 100644
index 0000000000..2cb92fda90
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/economy/vending.ftl
@@ -0,0 +1,2 @@
+vending-ui-credits-amount = Кредитов в автомате: { $credits }
+vending-machine-component-no-balance = Недостаточно средств.
diff --git a/Resources/Locale/ru-RU/ADT/prototypes/Entities/Objects/Device/cartridges.ftl b/Resources/Locale/ru-RU/ADT/prototypes/Entities/Objects/Device/cartridges.ftl
new file mode 100644
index 0000000000..a28c369925
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/prototypes/Entities/Objects/Device/cartridges.ftl
@@ -0,0 +1,3 @@
+ent-BankCartridge = картридж виртуального кошелька
+ .desc = Программа для управления банковским аккаунтом.
+ .suffix = { "" }
diff --git a/Resources/Locale/ru-RU/ADT/prototypes/Entities/Objects/Device/economy.ftl b/Resources/Locale/ru-RU/ADT/prototypes/Entities/Objects/Device/economy.ftl
new file mode 100644
index 0000000000..ea197e739a
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/prototypes/Entities/Objects/Device/economy.ftl
@@ -0,0 +1,27 @@
+ent-Eftpos = терминал безналичной оплаты
+ .desc = Проведите картой для совершения покупок без использования наличных.
+ .suffix = { "" }
+ent-CommandBudgetCard = ведомственная карточка (Станционный бюджет).
+ .desc = Предоставляет доступ к бюджету станции!
+ .suffix = { "" }
+ent-CargoBudgetCard = ведомственная карточка (Бюджет снабжения).
+ .desc = Предоставляет доступ к бюджету снабжения!
+ .suffix = { "" }
+ent-CivilianBudgetCard = ведомственная карточка (Гражданский бюджет).
+ .desc = Предоставляет доступ к гражданскому бюджету!
+ .suffix = { "" }
+ent-EngineeringBudgetCard = ведомственная карточка (Инженерный бюджет).
+ .desc = Предоставляет доступ к инженерному бюджету!
+ .suffix = { "" }
+ent-MedicalBudgetCard = ведомственная карточка (Медицинский бюджет).
+ .desc = Предоставляет доступ к медицинскому бюджету!
+ .suffix = { "" }
+ent-ScienceBudgetCard = ведомственная карточка (Научный бюджет).
+ .desc = Предоставляет доступ к научному бюджету!
+ .suffix = { "" }
+ent-SecurityBudgetCard = ведомственная карточка (Оборонный бюджет).
+ .desc = Предоставляет доступ к оборонному бюджету!
+ .suffix = { "" }
+ent-CommandBudgetPinPaper = PIN-код станционного бюджета.
+ .desc = Листок белой бумаги
+ .suffix = { "" }
diff --git a/Resources/Locale/ru-RU/ADT/prototypes/Entities/Structures/Machines/atm.ftl b/Resources/Locale/ru-RU/ADT/prototypes/Entities/Structures/Machines/atm.ftl
new file mode 100644
index 0000000000..66490b7e3c
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/prototypes/Entities/Structures/Machines/atm.ftl
@@ -0,0 +1,3 @@
+ent-ATM = банкомат
+ .desc = Величайшее достижение экономики. С помощью данного аппарата можно снимать деньги или же наоборот внести какую-то сумму на свой виртуальный кошелёк.
+ .suffix = { "" }
diff --git a/Resources/Prototypes/ADT/Economy/salary.yml b/Resources/Prototypes/ADT/Economy/salary.yml
new file mode 100644
index 0000000000..b91d5d0807
--- /dev/null
+++ b/Resources/Prototypes/ADT/Economy/salary.yml
@@ -0,0 +1,46 @@
+- type: salary
+ id: Salaries
+ salaries:
+ ADTBlueShieldOfficer: 1000
+ Captain: 800
+ Magistrat: 800
+ CentralCommandOfficial: 800
+ ChiefEngineer: 600
+ ChiefMedicalOfficer: 600
+ HeadOfPersonnel: 600
+ HeadOfSecurity: 600
+ ResearchDirector: 600
+ Quartermaster: 600
+ CargoTechnician: 200
+ SalvageSpecialist: 300
+ Bartender: 200
+ Botanist: 200
+ Boxer: 150
+ Chaplain: 150
+ Chef: 200
+ Clown: 150
+ Janitor: 200
+ Lawyer: 150
+ Librarian: 150
+ Mime: 150
+ Musician: 150
+ Passenger: 100
+ Reporter: 150
+ Zookeeper: 150
+ ServiceWorker: 150
+ AtmosphericTechnician: 400
+ StationEngineer: 300
+ TechnicalAssistant: 200
+ Chemist: 400
+ MedicalDoctor: 300
+ MedicalIntern: 200
+ Psychologist: 200
+ Paramedic: 400
+ SecurityCadet: 200
+ SecurityOfficer: 300
+ Detective: 400
+ Warden: 500
+ ADTPathologist: 400
+ ADTRoboticist: 400
+ Scientist: 300
+ ResearchAssistant: 200
diff --git a/Resources/Prototypes/ADT/Entities/Objects/Devices/cartridges.yml b/Resources/Prototypes/ADT/Entities/Objects/Devices/cartridges.yml
new file mode 100644
index 0000000000..9116fa6556
--- /dev/null
+++ b/Resources/Prototypes/ADT/Entities/Objects/Devices/cartridges.yml
@@ -0,0 +1,17 @@
+- type: entity
+ parent: BaseItem
+ id: ADTWalletCartridge
+ name: virtual wallet cartridge
+ description: A program for managing bank account
+ components:
+ - type: Sprite
+ sprite: Objects/Devices/cartridge.rsi
+ state: cart-y
+ - type: UIFragment
+ ui: !type:BankUi
+ - type: Cartridge
+ programName: bank-program-name
+ icon:
+ sprite: ADT/Structures/Machines/atm.rsi
+ state: off
+ - type: BankCartridge
diff --git a/Resources/Prototypes/ADT/Entities/Objects/Devices/economy.yml b/Resources/Prototypes/ADT/Entities/Objects/Devices/economy.yml
new file mode 100644
index 0000000000..6d6b6da7f0
--- /dev/null
+++ b/Resources/Prototypes/ADT/Entities/Objects/Devices/economy.yml
@@ -0,0 +1,125 @@
+- type: entity
+ parent: BaseItem
+ id: ADTBaseDepartmentBudgetCard
+ noSpawn: true
+ components:
+ - type: BankCard
+ startingBalance: 5000
+ - type: Sprite
+ sprite: ADT/Objects/Misc/cards.rsi
+ scale: 0.8, 0.8
+ state: budgetcard
+ - type: Item
+ storedRotation: 90
+
+- type: entity
+ parent: ADTBaseDepartmentBudgetCard
+ id: ADTCommandBudgetCard
+ name: command budget card
+ components:
+ - type: Sprite
+ state: budgetcard
+ - type: BankCard
+ commandBudgetCard: true
+
+- type: entity
+ parent: Paper
+ id: ADTCommandBudgetPinPaper
+ name: command budget pin
+ components:
+ - type: CommandBudgetPinPaper
+ - type: Paper
+
+- type: entity
+ parent: ADTBaseDepartmentBudgetCard
+ id: ADTCargoBudgetCard
+ noSpawn: true
+ name: cargo budget card
+ components:
+ - type: Sprite
+ state: car_budget
+ - type: BankCard
+ accountId: 1315
+
+- type: entity
+ parent: ADTBaseDepartmentBudgetCard
+ id: ADTCivilianBudgetCard
+ name: civilian budget card
+ noSpawn: true
+ components:
+ - type: Sprite
+ state: srv_budget
+ - type: BankCard
+ accountId: 1316
+
+- type: entity
+ parent: ADTBaseDepartmentBudgetCard
+ id: ADTEngineeringBudgetCard
+ name: engineering budget card
+ noSpawn: true
+ components:
+ - type: Sprite
+ state: eng_budget
+ - type: BankCard
+ accountId: 1318
+
+- type: entity
+ parent: ADTBaseDepartmentBudgetCard
+ id: ADTMedicalBudgetCard
+ name: medical budget card
+ noSpawn: true
+ components:
+ - type: Sprite
+ state: med_budget
+ - type: BankCard
+ accountId: 1319
+
+- type: entity
+ parent: ADTBaseDepartmentBudgetCard
+ id: ADTScienceBudgetCard
+ name: science budget card
+ noSpawn: true
+ components:
+ - type: Sprite
+ state: sci_budget
+ - type: BankCard
+ accountId: 1321
+
+- type: entity
+ parent: ADTBaseDepartmentBudgetCard
+ id: ADTSecurityBudgetCard
+ name: security budget card
+ noSpawn: true
+ components:
+ - type: Sprite
+ state: sec_budget
+ - type: BankCard
+ accountId: 1320
+
+- type: entity
+ parent: BaseItem
+ id: ADTEftpos
+ name: EFTPOS
+ description: Swipe your ID card to make purchases electronically.
+ components:
+ - type: Sprite
+ sprite: ADT/Objects/Devices/eftpos.rsi
+ state:
+ layers:
+ - state: eftpos
+ - state: eftpos-screen
+ shader: unshaded
+ netsync: false
+ - type: Eftpos
+ - type: ActivatableUI
+ key: enum.EftposKey.Key
+ singleUser: true
+ closeOnHandDeselect: false
+ - type: UserInterface
+ interfaces:
+ enum.EftposKey.Key:
+ type: EftposBui
+ - type: Clothing
+ quickEquip: false
+ slots:
+ - Belt
diff --git a/Resources/Prototypes/ADT/Entities/Structures/Machines/atm.yml b/Resources/Prototypes/ADT/Entities/Structures/Machines/atm.yml
new file mode 100644
index 0000000000..18e6e5e576
--- /dev/null
+++ b/Resources/Prototypes/ADT/Entities/Structures/Machines/atm.yml
@@ -0,0 +1,66 @@
+- type: entity
+ id: ATM
+ name: ATM
+ description: For all your monetary needs!
+ placement:
+ mode: SnapgridCenter
+ snap:
+ - Wallmount
+ components:
+ - type: WallMount
+ arc: 360
+ - type: Transform
+ anchored: true
+ - type: Clickable
+ - type: InteractionOutline
+ - type: Sprite
+ sprite: ADT/Structures/Machines/atm.rsi
+ snapCardinals: true
+ state:
+ layers:
+ - state: "off"
+ map: ["enum.ATMVisualLayers.Base"]
+ - state: "on"
+ map: ["enum.ATMVisualLayers.BaseUnshaded"]
+ shader: unshaded
+ - type: ATM
+ idCardSlot:
+ ejectSound: /Audio/Machines/id_swipe.ogg
+ insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg
+ ejectOnBreak: true
+ swap: false
+ whitelist:
+ components:
+ - BankCard
+ - type: ItemSlots
+ - type: ContainerContainer
+ containers:
+ IdCardSlot: !type:ContainerSlot
+ - type: Appearance
+ - type: AmbientOnPowered
+ - type: AmbientSound
+ volume: -9
+ range: 3
+ enabled: false
+ sound:
+ path: /Audio/Ambience/Objects/vending_machine_hum.ogg
+ - type: ActivatableUI
+ key: enum.ATMUiKey.Key
+ singleUser: true
+ - type: ActivatableUIRequiresPower
+ - type: UserInterface
+ interfaces:
+ enum.ATMUiKey.Key:
+ type: ATMBui
+ - type: PointLight
+ enabled: false
+ castShadows: false
+ radius: 1.5
+ - type: LitOnPowered
+ - type: ApcPowerReceiver
+ powerLoad: 200
+ priority: Low
+ - type: ExtensionCableReceiver
+ - type: EmitSoundOnUIOpen
+ sound:
+ collection: Keyboard
diff --git a/Resources/Prototypes/ADT/Store/currency.yml b/Resources/Prototypes/ADT/Store/currency.yml
new file mode 100644
index 0000000000..1275954185
--- /dev/null
+++ b/Resources/Prototypes/ADT/Store/currency.yml
@@ -0,0 +1,6 @@
+
+- type: currency
+ id: SpaceCash
+ cash :
+ 1: SpaceCash
+ displayName: store-currency-display-space-cash
diff --git a/Resources/Prototypes/ADT/tags.yml b/Resources/Prototypes/ADT/tags.yml
index aa6cfab49a..cf46c491d0 100644
--- a/Resources/Prototypes/ADT/tags.yml
+++ b/Resources/Prototypes/ADT/tags.yml
@@ -9,3 +9,9 @@
- type: Tag
id: ADTLasgunRevolverCell
+
+- type: Tag
+ id: IgnoreBalanceChecks
+
+- type: Tag
+ id: SpaceCash
diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml
index b6c95f9866..2303c4e253 100644
--- a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml
+++ b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml
@@ -21,6 +21,10 @@
- id: ClothingHeadsetAltCargo
- id: BoxEncryptionKeyCargo
- id: PrinterDocFlatpack # Corvax-Printer
+ # ADT-Economy-Start
+ - id: ADTCommandBudgetCard
+ - id: ADTCommandBudgetPinPaper
+ # ADT-Economy-End
- type: entity
id: LockerCaptainFilledHardsuit
@@ -49,6 +53,10 @@
- id: WeaponAntiqueLaser
- id: JetpackCaptainFilled
- id: MedalCase
+ # ADT-Economy-Start
+ - id: ADTCommandBudgetCard
+ - id: ADTCommandBudgetPinPaper
+ # ADT-Economy-End
- type: entity
id: LockerCaptainFilled
@@ -79,6 +87,10 @@
- id: ClothingHeadCaptainHat
- id: ClothingOuterCoatCaptain
# Corvax-Resprite-End
+ # ADT-Economy-Start
+ - id: ADTCommandBudgetCard
+ - id: ADTCommandBudgetPinPaper
+ # ADT-Economy-End
- type: entity
id: LockerCaptainFilledNoLaser
@@ -104,6 +116,10 @@
- id: RubberStampCaptain
- id: JetpackCaptainFilled
- id: MedalCase
+ # ADT-Economy-Start
+ - id: ADTCommandBudgetCard
+ - id: ADTCommandBudgetPinPaper
+ # ADT-Economy-End
- type: entity
id: LockerHeadOfPersonnelFilled
@@ -135,6 +151,10 @@
- id: ClothingOuterCoatHOP # Corvax-Resprite
- id: AccessConfigurator
- id: PrinterDocFlatpack # Corvax-Printer
+ # ADT-Economy-Start
+ - id: ADTCommandBudgetCard
+ - id: ADTCommandBudgetPinPaper
+ # ADT-Economy-End
- type: entity
id: LockerChiefEngineerFilledHardsuit
diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml
index f0f2c8016b..38cacd4561 100644
--- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml
+++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml
@@ -33,6 +33,7 @@
- NuclearOperative
- SyndicateAgent
- CentralCommand
+ - IgnoreBalanceChecks # ADT-Economy-Tweak
- type: UserInterface
interfaces:
enum.SolarControlConsoleUiKey.Key:
diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
index 04b023248e..8053536038 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
@@ -75,6 +75,7 @@
- CrewManifestCartridge
- NotekeeperCartridge
- NewsReaderCartridge
+ - ADTWalletCartridge # ADT-Economy
cartridgeSlot:
priority: -1
name: device-pda-slot-component-slot-name-cartridge
diff --git a/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml b/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml
index 57dfb40098..9a46352559 100644
--- a/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml
+++ b/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml
@@ -44,6 +44,11 @@
mask:
- ItemMask
- type: Appearance
+ # ADT-Economy-Start
+ - type: Currency
+ price:
+ SpaceCash: 1
+ # ADT-Economy-End
- type: material
id: Credit
diff --git a/Resources/Textures/ADT/Objects/Devices/eftpos.rsi/eftpos-screen.png b/Resources/Textures/ADT/Objects/Devices/eftpos.rsi/eftpos-screen.png
new file mode 100644
index 0000000000..7801077591
Binary files /dev/null and b/Resources/Textures/ADT/Objects/Devices/eftpos.rsi/eftpos-screen.png differ
diff --git a/Resources/Textures/ADT/Objects/Devices/eftpos.rsi/eftpos.png b/Resources/Textures/ADT/Objects/Devices/eftpos.rsi/eftpos.png
new file mode 100644
index 0000000000..776a048d21
Binary files /dev/null and b/Resources/Textures/ADT/Objects/Devices/eftpos.rsi/eftpos.png differ
diff --git a/Resources/Textures/ADT/Objects/Devices/eftpos.rsi/meta.json b/Resources/Textures/ADT/Objects/Devices/eftpos.rsi/meta.json
new file mode 100644
index 0000000000..50041fb1ae
--- /dev/null
+++ b/Resources/Textures/ADT/Objects/Devices/eftpos.rsi/meta.json
@@ -0,0 +1,17 @@
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-NC-SA-3.0",
+ "copyright": "Taken from https://github.com/Baystation12/Baystation12 at commit ec023cc3f8f7f170ec30d1a2cc32bd973c3d21fc.",
+ "states": [
+ {
+ "name": "eftpos"
+ },
+ {
+ "name": "eftpos-screen"
+ }
+ ]
+}
diff --git a/Resources/Textures/ADT/Objects/Misc/cards.rsi/budgetcard.png b/Resources/Textures/ADT/Objects/Misc/cards.rsi/budgetcard.png
new file mode 100644
index 0000000000..d9606876a5
Binary files /dev/null and b/Resources/Textures/ADT/Objects/Misc/cards.rsi/budgetcard.png differ
diff --git a/Resources/Textures/ADT/Objects/Misc/cards.rsi/car_budget.png b/Resources/Textures/ADT/Objects/Misc/cards.rsi/car_budget.png
new file mode 100644
index 0000000000..95bce7601f
Binary files /dev/null and b/Resources/Textures/ADT/Objects/Misc/cards.rsi/car_budget.png differ
diff --git a/Resources/Textures/ADT/Objects/Misc/cards.rsi/civ_budget.png b/Resources/Textures/ADT/Objects/Misc/cards.rsi/civ_budget.png
new file mode 100644
index 0000000000..fb457f5a15
Binary files /dev/null and b/Resources/Textures/ADT/Objects/Misc/cards.rsi/civ_budget.png differ
diff --git a/Resources/Textures/ADT/Objects/Misc/cards.rsi/eng_budget.png b/Resources/Textures/ADT/Objects/Misc/cards.rsi/eng_budget.png
new file mode 100644
index 0000000000..fd3e273503
Binary files /dev/null and b/Resources/Textures/ADT/Objects/Misc/cards.rsi/eng_budget.png differ
diff --git a/Resources/Textures/ADT/Objects/Misc/cards.rsi/med_budget.png b/Resources/Textures/ADT/Objects/Misc/cards.rsi/med_budget.png
new file mode 100644
index 0000000000..b9a0f21259
Binary files /dev/null and b/Resources/Textures/ADT/Objects/Misc/cards.rsi/med_budget.png differ
diff --git a/Resources/Textures/ADT/Objects/Misc/cards.rsi/meta.json b/Resources/Textures/ADT/Objects/Misc/cards.rsi/meta.json
new file mode 100644
index 0000000000..7a66d92854
--- /dev/null
+++ b/Resources/Textures/ADT/Objects/Misc/cards.rsi/meta.json
@@ -0,0 +1,35 @@
+{
+ "license": "CC-BY-SA-3.0",
+ "copyright": "???",
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "budgetcard"
+ },
+ {
+ "name": "car_budget"
+ },
+ {
+ "name": "srv_budget"
+ },
+ {
+ "name": "civ_budget"
+ },
+ {
+ "name": "sec_budget"
+ },
+ {
+ "name": "med_budget"
+ },
+ {
+ "name": "sci_budget"
+ },
+ {
+ "name": "eng_budget"
+ }
+ ]
+}
diff --git a/Resources/Textures/ADT/Objects/Misc/cards.rsi/sci_budget.png b/Resources/Textures/ADT/Objects/Misc/cards.rsi/sci_budget.png
new file mode 100644
index 0000000000..a4d966dc93
Binary files /dev/null and b/Resources/Textures/ADT/Objects/Misc/cards.rsi/sci_budget.png differ
diff --git a/Resources/Textures/ADT/Objects/Misc/cards.rsi/sec_budget.png b/Resources/Textures/ADT/Objects/Misc/cards.rsi/sec_budget.png
new file mode 100644
index 0000000000..c8d95d56b9
Binary files /dev/null and b/Resources/Textures/ADT/Objects/Misc/cards.rsi/sec_budget.png differ
diff --git a/Resources/Textures/ADT/Objects/Misc/cards.rsi/srv_budget.png b/Resources/Textures/ADT/Objects/Misc/cards.rsi/srv_budget.png
new file mode 100644
index 0000000000..323bfe49e1
Binary files /dev/null and b/Resources/Textures/ADT/Objects/Misc/cards.rsi/srv_budget.png differ
diff --git a/Resources/Textures/ADT/Structures/Machines/atm.rsi/meta.json b/Resources/Textures/ADT/Structures/Machines/atm.rsi/meta.json
new file mode 100644
index 0000000000..3b03dd6951
--- /dev/null
+++ b/Resources/Textures/ADT/Structures/Machines/atm.rsi/meta.json
@@ -0,0 +1,17 @@
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from https://github.com/Baystation12/Baystation12 at commit 0c2c999f74f2998912ae009223df83cbd987aa5d",
+ "states": [
+ {
+ "name": "off"
+ },
+ {
+ "name": "on"
+ }
+ ]
+}
diff --git a/Resources/Textures/ADT/Structures/Machines/atm.rsi/off.png b/Resources/Textures/ADT/Structures/Machines/atm.rsi/off.png
new file mode 100644
index 0000000000..209b9a6c0a
Binary files /dev/null and b/Resources/Textures/ADT/Structures/Machines/atm.rsi/off.png differ
diff --git a/Resources/Textures/ADT/Structures/Machines/atm.rsi/on.png b/Resources/Textures/ADT/Structures/Machines/atm.rsi/on.png
new file mode 100644
index 0000000000..02ac1c863d
Binary files /dev/null and b/Resources/Textures/ADT/Structures/Machines/atm.rsi/on.png differ