diff --git a/Content.Client/AWS/Economy/ClientEconomyManager.cs b/Content.Client/AWS/Economy/ClientEconomyManager.cs new file mode 100644 index 00000000000..961ba4f33bc --- /dev/null +++ b/Content.Client/AWS/Economy/ClientEconomyManager.cs @@ -0,0 +1,78 @@ +using System.Collections.Frozen; +using System.Diagnostics.CodeAnalysis; +using Content.Shared.AWS.Economy; +using Robust.Shared.Network; + +namespace Content.Client.AWS.Economy; + +public sealed class ClientEconomyManager : IClientEconomyManager +{ + [Dependency] private readonly IClientNetManager _netManager = default!; + + public event EventHandler? AccountUpdateReceived; + + /// + /// List of all cached accounts on this client. + /// + private FrozenDictionary _cachedAccounts = default!; + + public void Initialize() + { + _netManager.RegisterNetMessage(); + + _netManager.RegisterNetMessage(AccountsUpdateMessage); + } + + public bool IsValidAccount(string accountID) + { + AccountUpdateRequest(); + return _cachedAccounts.ContainsKey(accountID); + } + + public bool TryGetAccount(string accountID, [NotNullWhen(true)] out EconomyBankAccount? account) + { + AccountUpdateRequest(); + return _cachedAccounts.TryGetValue(accountID, out account); + } + + /// + /// Returns list of all cached accounts on this client. Use before using for fresh data. + /// + public IReadOnlyDictionary GetAccounts(EconomyBankAccountMask flag = EconomyBankAccountMask.NotBlocked) + { + if (flag == EconomyBankAccountMask.All) + return _cachedAccounts; + + Dictionary list = new(); + var accountsEnum = _cachedAccounts.GetEnumerator(); + while (accountsEnum.MoveNext()) + { + var account = accountsEnum.Current.Value; + switch (flag) + { + case EconomyBankAccountMask.NotBlocked: + if (!account.Blocked) + list.Add(account.AccountID, account); + break; + case EconomyBankAccountMask.Blocked: + if (account.Blocked) + list.Add(account.AccountID, account); + break; + } + } + + return list; + } + + public void AccountUpdateRequest() + { + var msg = new MsgEconomyAccountListRequest(); + _netManager.ClientSendMessage(msg); + } + + private void AccountsUpdateMessage(MsgEconomyAccountList message) + { + _cachedAccounts = message.Accounts.ToFrozenDictionary(); + AccountUpdateReceived?.Invoke(this, EventArgs.Empty); + } +} \ No newline at end of file diff --git a/Content.Client/AWS/Economy/IClientEconomyManager.cs b/Content.Client/AWS/Economy/IClientEconomyManager.cs new file mode 100644 index 00000000000..5d93bafe0bd --- /dev/null +++ b/Content.Client/AWS/Economy/IClientEconomyManager.cs @@ -0,0 +1,16 @@ +using Content.Shared.AWS.Economy; + +namespace Content.Client.AWS.Economy; + +public interface IClientEconomyManager : ISharedEconomyManager +{ + /// + /// Raised when the account list is received from the server. + /// + event EventHandler AccountUpdateReceived; + + /// + /// Requests the server to send the account list. + /// + void AccountUpdateRequest(); +} diff --git a/Content.Client/AWS/Economy/UI/EconomyLogConsoleBoundUserInterface.cs b/Content.Client/AWS/Economy/UI/EconomyLogConsoleBoundUserInterface.cs index 04463cfa280..5ddf5044956 100644 --- a/Content.Client/AWS/Economy/UI/EconomyLogConsoleBoundUserInterface.cs +++ b/Content.Client/AWS/Economy/UI/EconomyLogConsoleBoundUserInterface.cs @@ -1,26 +1,32 @@ -using Content.Shared.AWS.Economy; -using Content.Client.AWS.Economy.UI; - namespace Content.Client.AWS.Economy.UI; public sealed class EconomyLogConsoleBoundUserInterface : BoundUserInterface { + [Dependency] private readonly IClientEconomyManager _economyManager = default!; + [ViewVariables] private EconomyLogConsoleMenu? _menu; public EconomyLogConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { - + IoCManager.InjectDependencies(this); } protected override void Open() { base.Open(); + // Requset accounts from the server, and when we receive them, open the menu. + _economyManager.AccountUpdateRequest(); + _economyManager.AccountUpdateReceived += OnAccountUpdate; + } + + private void OnAccountUpdate(object? sender, EventArgs e) + { _menu = new EconomyLogConsoleMenu(this); _menu.OnClose += Close; - _menu.OpenCentered(); + _menu?.OpenCentered(); } protected override void Dispose(bool disposing) @@ -28,6 +34,8 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); if (!disposing) return; + + _economyManager.AccountUpdateReceived -= OnAccountUpdate; _menu?.Dispose(); } } diff --git a/Content.Client/AWS/Economy/UI/EconomyLogConsoleMenu.xaml.cs b/Content.Client/AWS/Economy/UI/EconomyLogConsoleMenu.xaml.cs index 8a6862199dd..8abb823b684 100644 --- a/Content.Client/AWS/Economy/UI/EconomyLogConsoleMenu.xaml.cs +++ b/Content.Client/AWS/Economy/UI/EconomyLogConsoleMenu.xaml.cs @@ -1,29 +1,31 @@ using Content.Client.UserInterface.Controls; using Content.Shared.AWS.Economy; -using Content.Client.AWS.Economy.UI; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.Controls; using System.Linq; +using System.Collections.Frozen; namespace Content.Client.AWS.Economy.UI; [GenerateTypedNameReferences] public sealed partial class EconomyLogConsoleMenu : FancyWindow { - [Dependency] private readonly IEntitySystemManager _entitySystem = default!; + [Dependency] private readonly IClientEconomyManager _economyManager = default!; + private EconomyLogConsoleBoundUserInterface Owner { get; set; } - private Dictionary _accounts; + private FrozenDictionary _accounts = default!; + public EconomyLogConsoleMenu(EconomyLogConsoleBoundUserInterface owner) { IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); - var economyBankAccount = _entitySystem.GetEntitySystem(); LogDetails.SelectMode = ItemList.ItemListSelectMode.None; Owner = owner; - _accounts = economyBankAccount.GetAccounts(); + + _accounts = _economyManager.GetAccounts().ToFrozenDictionary(); FillList(); @@ -36,16 +38,16 @@ private void OnSelectAccount(ItemList.Item accountId) { LogDetails.Clear(); - var accountComponent = (accountId.Metadata! as EconomyBankAccountComponent)!; + var account = (accountId.Metadata! as EconomyBankAccount)!; - if (accountComponent.Logs.Count == 0) + if (account.Logs.Count == 0) { LogDetails.AddItem("No logs detected"); return; } - for (int i = accountComponent.Logs.Count - 1; i != -1; i--) + for (int i = account.Logs.Count - 1; i != -1; i--) { - var item = accountComponent.Logs[i]; + var item = account.Logs[i]; LogDetails.AddItem("[" + item.Date.ToString("hh\\:mm\\:ss") + "] — " + item.Text); } } @@ -84,11 +86,11 @@ private void OnTextEnteredLog(LineEdit.LineEditEventArgs eventArgs) LogDetails.Clear(); var upText = eventArgs.Text.ToUpper(); - var accountComponent = (accountId.Metadata! as EconomyBankAccountComponent)!; + var account = (accountId.Metadata! as EconomyBankAccount)!; - for (int i = accountComponent.Logs.Count - 1; i != -1; i--) + for (int i = account.Logs.Count - 1; i != -1; i--) { - var item = accountComponent.Logs[i]; + var item = account.Logs[i]; if (item.Text.Contains(upText)) LogDetails.AddItem("[" + item.Date.ToString("hh\\:mm\\:ss") + "] — " + item.Text); } @@ -105,8 +107,8 @@ private void FillList() AccountList.SortItemsByText(); } - private string FormFieldName(EconomyBankAccountComponent comp) + private string FormFieldName(EconomyBankAccount account) { - return comp.AccountId + " — " + comp.AccountName; + return account.AccountID + " — " + account.AccountName; } } diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index d93f79fc239..2fef5d36a53 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -37,6 +37,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Replays; using Robust.Shared.Timing; +using Content.Shared.AWS.Economy; namespace Content.Client.Entry { @@ -73,6 +74,7 @@ public sealed class EntryPoint : GameClient [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly ContentReplayPlaybackManager _replayMan = default!; [Dependency] private readonly DebugMonitorManager _debugMonitorManager = default!; + [Dependency] private readonly ISharedEconomyManager _economyManager = default!; public override void Init() { @@ -134,6 +136,7 @@ public override void Init() _extendedDisconnectInformation.Initialize(); _jobRequirements.Initialize(); _playbackMan.Initialize(); + _economyManager.Initialize(); //AWS-economy //AUTOSCALING default Setup! _configManager.SetCVar("interface.resolutionAutoScaleUpperCutoffX", 1080); diff --git a/Content.Client/IoC/ClientContentIoC.cs b/Content.Client/IoC/ClientContentIoC.cs index 7c53393fc8e..a34094f8875 100644 --- a/Content.Client/IoC/ClientContentIoC.cs +++ b/Content.Client/IoC/ClientContentIoC.cs @@ -21,6 +21,8 @@ using Content.Client.Lobby; using Content.Shared.Administration.Managers; using Content.Shared.Players.PlayTimeTracking; +using Content.Shared.AWS.Economy; +using Content.Client.AWS.Economy; namespace Content.Client.IoC { @@ -52,6 +54,8 @@ public static void Register() collection.Register(); collection.Register(); collection.Register(); + collection.Register(); //AWS-economy + collection.Register(); //AWS-economy } } } diff --git a/Content.Server/AWS/Economy/EconomyBankAccountSystem.cs b/Content.Server/AWS/Economy/EconomyBankAccountSystem.cs index 59122d24cd7..d50dae46159 100644 --- a/Content.Server/AWS/Economy/EconomyBankAccountSystem.cs +++ b/Content.Server/AWS/Economy/EconomyBankAccountSystem.cs @@ -7,13 +7,16 @@ using Content.Server.Popups; using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Random; using Robust.Shared.Prototypes; using Content.Shared.Access.Components; using Content.Server.Access.Components; using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Timing; +using System.Diagnostics.CodeAnalysis; namespace Content.Server.AWS.Economy { @@ -25,15 +28,20 @@ public sealed class EconomyBankAccountSystem : EconomyBankAccountSystemShared [Dependency] private readonly VendingMachineSystem _vendingMachine = default!; [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IEconomyManager _economyManager = default!; public override void Initialize() { + SubscribeLocalEvent(OnAccountComponentInit); + SubscribeLocalEvent(OnTerminalInteracted); SubscribeLocalEvent(OnATMEmagged); SubscribeLocalEvent(OnATMInteracted); - - /*SubscribeLocalEvent(OnAccountComponentStartup);*/ + SubscribeLocalEvent(OnATMWithdrawMessage); + SubscribeLocalEvent(OnATMTransferMessage); } private string GenerateAccountId(string prefix, uint strik, uint numbersPerStrik, string? descriptor) @@ -55,29 +63,64 @@ private string GenerateAccountId(string prefix, uint strik, uint numbersPerStrik return res; } + /// + /// Enables a card or a bank account for usage. + /// [PublicAPI] public bool TryActivate(Entity entity) { - if (entity.Comp.ActivateOnSpawn) - { - entity.Comp.Activated = true; - return true; - } if (!_prototypeManager.TryIndex(entity.Comp.AccountIdByProto, out EconomyAccountIdPrototype? proto)) return false; - entity.Comp.AccountId = GenerateAccountId(proto.Prefix, proto.Strik, proto.NumbersPerStrik, proto.Descriptior); + var accountID = GenerateAccountId(proto.Prefix, proto.Streak, proto.NumbersPerStreak, proto.Descriptior); + var accountName = entity.Comp.AccountName; + var balance = (ulong)0; if (TryComp(entity, out var idCardComponent)) - entity.Comp.AccountName = idCardComponent.FullName ?? entity.Comp.AccountName; + accountName = idCardComponent.FullName ?? entity.Comp.AccountName; if (TryComp(entity, out var presetIdCardComponent)) if (_prototypeManager.TryIndex("NanotrasenDefaultSallaries", out var sallariesPrototype) && presetIdCardComponent.JobName is not null) if (sallariesPrototype.Jobs.TryGetValue(presetIdCardComponent.JobName.Value, out var entry)) - entity.Comp.Balance = (ulong)(entry.StartMoney * _robustRandom.NextDouble(0.5, 1.5)); + balance = (ulong)(entry.StartMoney * _robustRandom.NextDouble(0.5, 1.5)); + + if (entity.Comp.AccountSetup is { } setup && presetIdCardComponent is null && idCardComponent is null) + { + var setupID = setup.GenerateAccountID ? accountID : + setup.AccountID; + + if (setupID is null) + return false; + + var setupAccount = new EconomyBankAccount(setupID, + setup.AccountName, + setup.AllowedCurrency, + setup.Balance, + setup.Penalty, + setup.Blocked, + setup.CanReachPayDay); + + if (!_economyManager.TryAddAccount(setupAccount)) + return false; + + entity.Comp.AccountID = setupAccount.AccountID; + entity.Comp.AccountName = setupAccount.AccountName; + Dirty(entity); + return true; + } + if (presetIdCardComponent is not null && idCardComponent is not null) { - entity.Comp.Activated = true; + var accountSetup = entity.Comp.AccountSetup; + balance = accountSetup?.Balance ?? balance; + var account = new EconomyBankAccount(accountID, accountName, accountSetup?.AllowedCurrency, balance, + accountSetup?.Penalty, accountSetup?.Blocked, accountSetup?.CanReachPayDay); + + if (!_economyManager.TryAddAccount(account)) + return false; + + entity.Comp.AccountID = accountID; + entity.Comp.AccountName = accountName; Dirty(entity); return true; } @@ -85,6 +128,178 @@ public bool TryActivate(Entity entity) return false; } + private void Withdraw(EconomyBankAccountComponent component, EconomyBankATMComponent atm, ulong sum) + { + if (!_economyManager.TryChangeAccountBalance(component.AccountID, sum, false)) + return; + + var pos = _transformSystem.GetMapCoordinates(atm.Owner); + DropMoneyHandler(component.MoneyHolderEntId, sum, pos); + + if (_economyManager.TryGetAccount(component.AccountID, out var account)) + { + var log = new EconomyBankAccountLogField(_gameTiming.CurTime, Loc.GetString("economybanksystem-log-withdraw", + ("amount", sum), ("currencyName", account.AllowedCurrency))); + _economyManager.AddLog(component.AccountID, log); + } + + _entManager.Dirty(component); + } + + [PublicAPI] + public bool TryWithdraw(EconomyBankAccountComponent component, EconomyBankATMComponent atm, ulong sum, [NotNullWhen(false)] out string? errorMessage) + { + errorMessage = ""; + if (!_economyManager.TryGetAccount(component.AccountID, out var account)) + return false; + + if (sum > 0 && account.Balance >= sum) + { + Withdraw(component, atm, sum); + return true; + } + errorMessage = Loc.GetString("economybanksystem-transaction-error-notenoughmoney"); + return false; + } + + [PublicAPI] + public Entity DropMoneyHandler(EntProtoId entId, ulong amount, MapCoordinates pos) + { + var ent = Spawn(entId, pos); + + var moneyHolderComp = Comp(ent); + moneyHolderComp.Balance = amount; + + _entManager.Dirty(moneyHolderComp); + + return (ent, moneyHolderComp); + } + + [PublicAPI] + public bool TrySendMoney(IEconomyMoneyHolder fromAccount, EconomyBankAccountComponent? recipientAccount, ulong amount, [NotNullWhen(false)] out string? errorMessage) + { + errorMessage = null; + + if (fromAccount.Balance >= amount) + { + if (recipientAccount is not null) + { + return _economyManager.TryChangeAccountBalance(recipientAccount.AccountID, amount); + } + + errorMessage = Loc.GetString("economybanksystem-transaction-error-notfoundaccout"); + return false; + } + + errorMessage = Loc.GetString("economybanksystem-transaction-error-notenoughmoney"); + return false; + } + + [PublicAPI] + public bool TrySendMoney(IEconomyMoneyHolder fromAccount, string recipientAccountId, ulong amount, [NotNullWhen(false)] out string? errorMessage) + { + errorMessage = null; + + if (!_economyManager.IsValidAccount(recipientAccountId)) + { + errorMessage = Loc.GetString("economybanksystem-transaction-error-notfoundaccout", ("accountId", recipientAccountId)); + return false; + } + + if (fromAccount.Balance < amount) + { + errorMessage = Loc.GetString("economybanksystem-transaction-error-notenoughmoney"); + return false; + } + + return _economyManager.TryChangeAccountBalance(recipientAccountId, amount); + } + + [PublicAPI] + public bool TrySendMoney(EconomyBankAccountComponent fromAccount, string recipientAccountId, ulong amount, [NotNullWhen(false)] out string? errorMessage) + { + errorMessage = null; + + if (!_economyManager.TryGetAccount(fromAccount.AccountID, out var fromBankAccount)) + { + errorMessage = Loc.GetString("economybanksystem-transaction-error-notfoundaccout", ("accountId", fromAccount.AccountID)); + return false; + } + + if (!_economyManager.IsValidAccount(recipientAccountId)) + { + errorMessage = Loc.GetString("economybanksystem-transaction-error-notfoundaccout", ("accountId", recipientAccountId)); + return false; + } + + if (fromBankAccount.Balance < amount) + { + errorMessage = Loc.GetString("economybanksystem-transaction-error-notenoughmoney"); + return false; + } + + return _economyManager.TryTransferMoney(fromBankAccount.AccountID, recipientAccountId, amount); + } + + [PublicAPI] + public bool TrySendMoney(string fromAccountId, string recipientAccountId, ulong amount, [NotNullWhen(false)] out string? errorMessage) + { + errorMessage = null; + + if (!_economyManager.TryGetAccount(fromAccountId, out var fromBankAccount)) + { + errorMessage = Loc.GetString("economybanksystem-transaction-error-notfoundaccout", ("accountId", fromAccountId)); + return false; + } + + if (!_economyManager.IsValidAccount(recipientAccountId)) + { + errorMessage = Loc.GetString("economybanksystem-transaction-error-notfoundaccout", ("accountId", recipientAccountId)); + return false; + } + + if (fromBankAccount.Balance < amount) + { + errorMessage = Loc.GetString("economybanksystem-transaction-error-notenoughmoney"); + return false; + } + + return _economyManager.TryTransferMoney(fromAccountId, recipientAccountId, amount); + } + + private void OnAccountComponentInit(Entity entity, ref ComponentInit args) + { + // if has id card comp, then it will be initialized in other place + if (entity.Comp.AccountSetup is null || HasComp(entity)) + return; + + TryActivate(entity); + } + + private void OnATMWithdrawMessage(EntityUid uid, EconomyBankATMComponent atm, EconomyBankATMWithdrawMessage args) + { + var bankAccount = GetATMInsertedAccount(atm); + if (bankAccount is null) + return; + + string? error; + + TryWithdraw(bankAccount, atm, args.Amount, out error); + UpdateATMUserInterface((uid, atm), error); + } + + private void OnATMTransferMessage(EntityUid uid, EconomyBankATMComponent atm, EconomyBankATMTransferMessage args) + { + var bankAccount = GetATMInsertedAccount(atm); + if (bankAccount is null) + return; + + string? error; + + TrySendMoney(bankAccount, args.RecipientAccountId, args.Amount, out error); + UpdateATMUserInterface((uid, atm), error); + } + private void OnATMEmagged(EntityUid uid, EconomyBankATMComponent component, ref GotEmaggedEvent args) { if (HasComp(uid) || args.Handled) @@ -100,7 +315,7 @@ private void OnATMEmagged(EntityUid uid, EconomyBankATMComponent component, ref return; var moneyHolderCount = _robustRandom.Next(1, component.EmagDropMoneyHolderRandomCount + 1); - var mapPos = Comp(uid).MapPosition; + var mapPos = _transformSystem.GetMapCoordinates(uid); for (int i = 0; i < moneyHolderCount; i++) { @@ -124,6 +339,12 @@ private void OnATMInteracted(EntityUid uid, EconomyBankATMComponent component, I if (TrySendMoney(economyMoneyHolderComponent, insertedAccountComponent, amount, out var error)) { + if (insertedAccountComponent is not null && _economyManager.TryGetAccount(insertedAccountComponent.AccountID, out var account)) + _economyManager.AddLog(account.AccountID, + new EconomyBankAccountLogField(_gameTiming.CurTime, + Loc.GetString("economybanksystem-log-insert", + ("amount", amount), ("currencyName", account.AllowedCurrency)))); + if (_netManager.IsServer) _popupSystem.PopupEntity(Loc.GetString("economybanksystem-atm-moneyentering"), uid, type: PopupType.Medium); @@ -143,14 +364,10 @@ private void OnTerminalInteracted(EntityUid uid, EconomyBankTerminalComponent co if (amount <= 0) return; - TryComp(usedEnt, out var economyMoneyHolderComponent); - TryComp(usedEnt, out var economyBankAccountComponent); - - if (economyMoneyHolderComponent is null && economyBankAccountComponent is null) + if (!TryComp(usedEnt, out var economyMoneyHolderComponent) & + !TryComp(usedEnt, out var economyBankAccountComponent)) return; - IEconomyMoneyHolder anyComp = (economyMoneyHolderComponent is not null ? economyMoneyHolderComponent : economyBankAccountComponent)!; - if (TryComp(uid, out var vendingMachineComponent)) { if (vendingMachineComponent.SelectedItemId is not null) @@ -177,16 +394,31 @@ private void OnTerminalInteracted(EntityUid uid, EconomyBankTerminalComponent co } - if (!TrySendMoney(anyComp, component.LinkedAccount, amount, out var err)) - { - if (_netManager.IsServer) - _popupSystem.PopupEntity(err, uid, type: PopupType.MediumCaution); + if (!_economyManager.TryGetAccount(component.LinkedAccount, out var receiverAccount)) return; + + if (economyMoneyHolderComponent is not null) + { + if (!TrySendMoney(economyMoneyHolderComponent, component.LinkedAccount, amount, out var err)) + { + if (_netManager.IsServer) + _popupSystem.PopupEntity(err, uid, type: PopupType.MediumCaution); + return; + } + } + else if (economyBankAccountComponent is not null) + { + if (!TrySendMoney(economyBankAccountComponent, component.LinkedAccount, amount, out var err)) + { + if (_netManager.IsServer) + _popupSystem.PopupEntity(err, uid, type: PopupType.MediumCaution); + return; + } } UpdateTerminal((uid, component), 0, string.Empty); - _popupSystem.PopupEntity(Loc.GetString("economybanksystem-transaction-success", ("amount", amount), ("currencyName", FindAccountById(component.LinkedAccount)!.AllowCurrency)), uid, type: PopupType.Medium); + _popupSystem.PopupEntity(Loc.GetString("economybanksystem-transaction-success", ("amount", amount), ("currencyName", receiverAccount.AllowedCurrency)), uid, type: PopupType.Medium); } } } diff --git a/Content.Server/AWS/Economy/EconomyManager.cs b/Content.Server/AWS/Economy/EconomyManager.cs new file mode 100644 index 00000000000..cf027045691 --- /dev/null +++ b/Content.Server/AWS/Economy/EconomyManager.cs @@ -0,0 +1,144 @@ +using System.Collections.Frozen; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using Content.Shared.AWS.Economy; +using Robust.Server.Player; +using Robust.Shared.Enums; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.Shared.Timing; + +namespace Content.Server.AWS.Economy; + +public sealed class EconomyManager : IEconomyManager +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IServerNetManager _netManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + + private Dictionary _accounts = new(); + + public void Initialize() + { + _netManager.RegisterNetMessage(); + + _netManager.RegisterNetMessage(AccountListUpdateRequest); + + _playerManager.PlayerStatusChanged += OnPlayerJoinedGame; + } + + public bool TryAddAccount(EconomyBankAccount account) + { + // Maybe do other checks here as well? + // If account already exists, return false. + if (IsValidAccount(account.AccountID)) + return false; + + return _accounts.TryAdd(account.AccountID, account); + } + + public bool TryChangeAccountBalance(string accountID, ulong amount, bool addition = true) + { + if (!TryGetAccount(accountID, out var account)) + return false; + + if (!addition) + { + if (account.Balance - amount < 0) + return false; + + account.Balance -= amount; + return true; + } + + account.Balance += amount; + return true; + } + + public bool TryTransferMoney(string senderID, string receiverID, ulong amount) + { + if (amount <= 0 || + !TryGetAccount(senderID, out var sender) || + !TryGetAccount(receiverID, out var receiver)) + return false; + + if (sender.Balance < amount) + return false; + + sender.Balance -= amount; + sender.Logs.Add(new(_gameTiming.CurTime, Loc.GetString("economybanksystem-log-send-to", + ("amount", amount), ("currencyName", receiver.AllowedCurrency), ("accountId", receiver.AccountID)))); + receiver.Balance += amount; + receiver.Logs.Add(new(_gameTiming.CurTime, Loc.GetString("economybanksystem-log-send-from", + ("amount", amount), ("currencyName", receiver.AllowedCurrency), ("accountId", sender.AccountID)))); + return true; + } + + public bool IsValidAccount(string accountID) + { + return _accounts.ContainsKey(accountID); + } + + public bool TryGetAccount(string accountID, [NotNullWhen(true)] out EconomyBankAccount? account) + { + return _accounts.TryGetValue(accountID, out account); + } + + public void AddLog(string accountID, EconomyBankAccountLogField log) + { + if (!TryGetAccount(accountID, out var account)) + return; + + account.Logs.Add(log); + } + + public IReadOnlyDictionary GetAccounts(EconomyBankAccountMask flag = EconomyBankAccountMask.NotBlocked) + { + if (flag == EconomyBankAccountMask.All) + { + ReadOnlyDictionary all = new(_accounts); + return all; + } + + Dictionary list = new(); + var accountsEnum = _accounts.GetEnumerator(); + while (accountsEnum.MoveNext()) + { + var account = accountsEnum.Current.Value; + switch (flag) + { + case EconomyBankAccountMask.NotBlocked: + if (!account.Blocked) + list.Add(account.AccountID, account); + break; + case EconomyBankAccountMask.Blocked: + if (account.Blocked) + list.Add(account.AccountID, account); + break; + } + } + + ReadOnlyDictionary result = new(list); + return result; + } + + private void UpdateAccountList(INetChannel channel) + { + var msg = new MsgEconomyAccountList(); + msg.Accounts = _accounts.ToFrozenDictionary(); + _netManager.ServerSendMessage(msg, channel); + } + + private void AccountListUpdateRequest(MsgEconomyAccountListRequest message) + { + UpdateAccountList(message.MsgChannel); + } + + private void OnPlayerJoinedGame(object? sender, SessionStatusEventArgs e) + { + if (e.NewStatus != SessionStatus.InGame) + return; + + UpdateAccountList(e.Session.Channel); + } +} \ No newline at end of file diff --git a/Content.Server/AWS/Economy/EconomyPayDayRule.cs b/Content.Server/AWS/Economy/EconomyPayDayRule.cs index ff0b21fd2f5..70979dfb35e 100644 --- a/Content.Server/AWS/Economy/EconomyPayDayRule.cs +++ b/Content.Server/AWS/Economy/EconomyPayDayRule.cs @@ -1,9 +1,4 @@ -using System.Linq; -using Content.Shared.Dataset; -using Content.Server.Ghost.Roles.Components; -using Content.Server.StationEvents.Components; using Content.Shared.GameTicking.Components; -using Content.Shared.Random.Helpers; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Content.Shared.AWS.Economy; @@ -11,19 +6,22 @@ using Content.Shared.Roles; using Content.Shared.Mind.Components; using Content.Shared.Roles.Jobs; +using Content.Server.AWS.Economy; namespace Content.Server.StationEvents.Events; public sealed class EconomyPayDayRule : StationEventSystem { [Dependency] private readonly IEntityManager _entMan = default!; - [Dependency] private readonly EconomyBankAccountSystemShared _bankAccountSystem = default!; + [Dependency] private readonly EconomyBankAccountSystem _bankAccountSystem = default!; + [Dependency] private readonly IEconomyManager _economyManager = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IRobustRandom _random = default!; + protected override void Started(EntityUid uid, EconomyPayDayRuleComponent ruleComponent, GameRuleComponent gameRule, GameRuleStartedEvent args) { - var accounts = _bankAccountSystem.GetAccounts(); - if (!accounts.TryGetValue(ruleComponent.PayerAccountId, out var payerComp)) + var accounts = _economyManager.GetAccounts(); + if (!_economyManager.TryGetAccount(ruleComponent.PayerAccountId, out var payerAccount)) return; if (!_prototype.TryIndex(ruleComponent.SallaryProto, out var sallariesProto)) @@ -41,17 +39,17 @@ protected override void Started(EntityUid uid, EconomyPayDayRuleComponent ruleCo manifest.Add(mindComponent.CharacterName, jobComponent.Prototype.Value); } } - List blockedAccounts = new(); + List blockedAccounts = new(); - foreach (var (id, comp) in accounts) + foreach (var (id, account) in accounts) { - if (comp.Blocked || !comp.CanReachPayDay) + if (account.Blocked || !account.CanReachPayDay) continue; - if (comp.Blocked) + if (account.Blocked) continue; - if (!manifest.TryGetValue(comp.AccountName, out var job)) + if (!manifest.TryGetValue(account.AccountName, out var job)) continue; EconomySallariesJobEntry? entry = null; @@ -71,13 +69,13 @@ protected override void Started(EntityUid uid, EconomyPayDayRuleComponent ruleCo switch (ruleComponent.PayType) { case EconomyPayDayRuleType.Adding: - _bankAccountSystem.TrySendMoney(payerComp, comp, sallary, out err); + _bankAccountSystem.TrySendMoney(payerAccount.AccountID, account.AccountID, sallary, out err); break; case EconomyPayDayRuleType.Decrementing: - if (!_bankAccountSystem.TrySendMoney(comp, payerComp, sallary, out err)) + if (!_bankAccountSystem.TrySendMoney(account.AccountID, payerAccount.AccountID, sallary, out err)) { - comp.Blocked = true; - blockedAccounts.Add(comp); + account.Blocked = true; + blockedAccounts.Add(account); } break; default: diff --git a/Content.Server/AWS/Economy/IEconomyManager.cs b/Content.Server/AWS/Economy/IEconomyManager.cs new file mode 100644 index 00000000000..6b25890bfe3 --- /dev/null +++ b/Content.Server/AWS/Economy/IEconomyManager.cs @@ -0,0 +1,30 @@ +using Content.Shared.AWS.Economy; + +namespace Content.Server.AWS.Economy; + +public interface IEconomyManager : ISharedEconomyManager +{ + /// + /// Adds account to the account list. + /// + /// True if the account was successfully added, false otherwise. + bool TryAddAccount(EconomyBankAccount account); + + /// + /// Changes the balance of the account. + /// + /// Whether to add or substract the given amount. + /// + bool TryChangeAccountBalance(string accountID, ulong amount, bool addition = true); + + /// + /// Transfer money from one account to another. + /// + /// True if the transfer was successful, false otherwise. + bool TryTransferMoney(string senderID, string receiverID, ulong amount); + + /// + /// Adds a log to the account. + /// + void AddLog(string accountID, EconomyBankAccountLogField log); +} \ No newline at end of file diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs index 02a125bdee2..6d1af9ff297 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -25,6 +25,7 @@ using Content.Server.ServerInfo; using Content.Server.ServerUpdates; using Content.Server.Voting.Managers; +using Content.Shared.AWS.Economy; using Content.Shared.CCVar; using Content.Shared.Kitchen; using Content.Shared.Localizations; @@ -115,6 +116,7 @@ public override void Init() _playTimeTracking.Initialize(); IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); + IoCManager.Resolve().Initialize(); //AWS-economy } } diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index 01497efae4a..cfd4ebe7624 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -3,6 +3,7 @@ using Content.Server.Administration.Managers; using Content.Server.Administration.Notes; using Content.Server.Afk; +using Content.Server.AWS.Economy; using Content.Server.Chat.Managers; using Content.Server.Connection; using Content.Server.Corvax.TTS; @@ -27,6 +28,7 @@ using Content.Server.Worldgen.Tools; using Content.Shared.Administration.Logs; using Content.Shared.Administration.Managers; +using Content.Shared.AWS.Economy; using Content.Shared.Kitchen; using Content.Shared.Players.PlayTimeTracking; @@ -71,6 +73,8 @@ public static void Register() IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); //AWS-economy + IoCManager.Register(); //AWS-economy } } } diff --git a/Content.Shared/AWS/Economy/BankAccountSetup.cs b/Content.Shared/AWS/Economy/BankAccountSetup.cs new file mode 100644 index 00000000000..649a17bb014 --- /dev/null +++ b/Content.Shared/AWS/Economy/BankAccountSetup.cs @@ -0,0 +1,32 @@ +using Content.Shared.Store; +using Robust.Shared.Prototypes; + +namespace Content.Shared.AWS.Economy; + +[DataDefinition] +public sealed partial class BankAccountSetup +{ + [DataField("accountID")] + public string? AccountID; + + [DataField("generateAccountID")] + public bool GenerateAccountID = false; + + [DataField("accountName")] + public string? AccountName; + + [DataField("allowedCurrency")] + public ProtoId? AllowedCurrency; + + [DataField("balance")] + public ulong? Balance; + + [DataField("penalty")] + public ulong? Penalty; + + [DataField("blocked")] + public bool? Blocked; + + [DataField("canReachPayDay")] + public bool? CanReachPayDay; +} \ No newline at end of file diff --git a/Content.Shared/AWS/Economy/EconomyAccountId.cs b/Content.Shared/AWS/Economy/EconomyAccountId.cs index 93d8cab3e89..de15d7440fe 100644 --- a/Content.Shared/AWS/Economy/EconomyAccountId.cs +++ b/Content.Shared/AWS/Economy/EconomyAccountId.cs @@ -13,9 +13,9 @@ public sealed partial class EconomyAccountIdPrototype : IPrototype [DataField(required: false)] public string? Descriptior; [DataField(required: false)] - public uint Strik = 4; + public uint Streak = 4; [DataField(required: false)] - public uint NumbersPerStrik = 4; + public uint NumbersPerStreak = 4; [DataField(required: false)] public uint[] MinMaxSallary = {0,0}; diff --git a/Content.Shared/AWS/Economy/EconomyBankAccount.cs b/Content.Shared/AWS/Economy/EconomyBankAccount.cs new file mode 100644 index 00000000000..2cbf310c9bd --- /dev/null +++ b/Content.Shared/AWS/Economy/EconomyBankAccount.cs @@ -0,0 +1,41 @@ +using Content.Shared.Store; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.AWS.Economy; + +[Serializable, NetSerializable] +public sealed class EconomyBankAccount +{ + public string AccountID { get; set; } + + public string AccountName { get; set; } + + public ProtoId AllowedCurrency { get; set; } + + public ulong Balance { get; set; } + + public ulong Penalty { get; set; } + + public bool Blocked { get; set; } + + public bool CanReachPayDay { get; set; } + + public List Logs { get; set; } + + public EconomyBankAccount(string accountID, string? accountName, + string? allowedCurrency, + ulong? balance, ulong? penalty, + bool? blocked, bool? canReachPayday, + List? logs = default) + { + AccountID = accountID; + AccountName = accountName ?? "UNEXPECTED USER"; + AllowedCurrency = allowedCurrency ?? "Thaler"; + Balance = balance ?? 0; + Penalty = penalty ?? 0; + Blocked = blocked ?? false; + CanReachPayDay = canReachPayday ?? true; + Logs = logs ?? []; + } +} \ No newline at end of file diff --git a/Content.Shared/AWS/Economy/EconomyBankAccountComponent.cs b/Content.Shared/AWS/Economy/EconomyBankAccountComponent.cs index f7f9b41fa93..1e9eac69198 100644 --- a/Content.Shared/AWS/Economy/EconomyBankAccountComponent.cs +++ b/Content.Shared/AWS/Economy/EconomyBankAccountComponent.cs @@ -1,53 +1,38 @@ using Robust.Shared.Prototypes; -using Content.Shared.Store; using Robust.Shared.GameStates; using Robust.Shared.Serialization; namespace Content.Shared.AWS.Economy { [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] - public sealed partial class EconomyBankAccountComponent : Component, IEconomyMoneyHolder + public sealed partial class EconomyBankAccountComponent : Component { - [ViewVariables(VVAccess.ReadWrite), DataField(required: true)] - public ProtoId AllowCurrency = "Thaler"; [ViewVariables(VVAccess.ReadWrite), DataField(required: true)] public ProtoId AccountIdByProto = "Nanotrasen"; + [ViewVariables(VVAccess.ReadWrite), DataField(required: true)] public EntProtoId MoneyHolderEntId = "ThalerHolder"; + /// + /// Set this up in EconomyBankAccountSetup to define the account, which this card will be using (referring to). + /// [ViewVariables(VVAccess.ReadWrite), DataField] [AutoNetworkedField] - public ulong Balance { get; set; } = 0; - [ViewVariables(VVAccess.ReadWrite), DataField] - [AutoNetworkedField] - public ulong Penalty = 0; - - [ViewVariables(VVAccess.ReadWrite), DataField] - [AutoNetworkedField] - public string AccountId = "NO VALUE"; + public string AccountID = "NO VALUE"; + /// + /// Set this up in EconomyBankAccountSetup to define the name, which this card will be using. + /// [ViewVariables(VVAccess.ReadWrite), DataField] [AutoNetworkedField] public string AccountName = "UNEXPECTED USER"; - [ViewVariables(VVAccess.ReadWrite)] - [AutoNetworkedField] - public bool Blocked = false; - + /// + /// Use this in prototypes for defining the account, which this card will be using (the account will be initialized on spawn). + /// Also, parameters beyond AccountName can be used with IDCards (if you want to setup other currency, for example). + /// [ViewVariables(VVAccess.ReadOnly), DataField] - public bool ActivateOnSpawn = false; - - [ViewVariables(VVAccess.ReadWrite)] - [AutoNetworkedField] - public bool Activated = false; - - [ViewVariables(VVAccess.ReadWrite)] - [AutoNetworkedField] - public bool CanReachPayDay = true; - - [ViewVariables(VVAccess.ReadWrite)] - [AutoNetworkedField] - public List Logs = new(); + public BankAccountSetup? AccountSetup; } [Serializable, NetSerializable] diff --git a/Content.Shared/AWS/Economy/EconomyBankAccountSystemShared.cs b/Content.Shared/AWS/Economy/EconomyBankAccountSystemShared.cs index de4f73d7c45..ac6bda32b91 100644 --- a/Content.Shared/AWS/Economy/EconomyBankAccountSystemShared.cs +++ b/Content.Shared/AWS/Economy/EconomyBankAccountSystemShared.cs @@ -1,18 +1,7 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Robust.Shared.Utility; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; using Content.Shared.Containers.ItemSlots; -using Content.Shared.Access.Components; using Content.Shared.Examine; using Robust.Shared.Containers; -using Robust.Shared.Map; -using Content.Shared.Interaction; -using Content.Shared.VendingMachines; using Content.Shared.Popups; -using Robust.Shared.Network; -using Robust.Shared.Timing; using JetBrains.Annotations; namespace Content.Shared.AWS.Economy @@ -20,12 +9,10 @@ namespace Content.Shared.AWS.Economy public class EconomyBankAccountSystemShared : EntitySystem { [Dependency] protected readonly EntityManager _entManager = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly SharedUserInterfaceSystem _userInterfaceSystem = default!; + [Dependency] private readonly ISharedEconomyManager _economyManager = default!; public override void Initialize() { @@ -33,7 +20,6 @@ public override void Initialize() SubscribeLocalEvent(OnBankTerminalExamine); SubscribeLocalEvent(OnTerminalMessage); - SubscribeLocalEvent(OnBankAccountComponentInit); SubscribeLocalEvent(OnBankAccountExamine); SubscribeLocalEvent(OnMoneyHolderExamine); @@ -41,26 +27,18 @@ public override void Initialize() SubscribeLocalEvent(OnATMComponentRemove); SubscribeLocalEvent(OnATMItemSlotChanged); SubscribeLocalEvent(OnATMItemSlotChanged); - SubscribeLocalEvent(OnATMWithdrawMessage); - SubscribeLocalEvent(OnATMTransferMessage); - } - - private void OnBankAccountComponentInit(EntityUid uid, EconomyBankAccountComponent comp, ComponentInit args) - { - if (comp.ActivateOnSpawn) - { - comp.Activated = true; - Dirty(uid, comp); - } } private void OnBankAccountExamine(Entity entity, ref ExaminedEvent args) { + if (!_economyManager.TryGetAccount(entity.Comp.AccountID, out var account)) + return; + args.PushMarkup(Loc.GetString("bankaccount-component-on-examine-detailed-message", - ("id", entity.Comp.AccountId))); + ("id", account.AccountID))); args.PushMarkup(Loc.GetString("moneyholder-component-on-examine-detailed-message", - ("moneyName", entity.Comp.AllowCurrency), - ("balance", entity.Comp.Balance))); + ("moneyName", account.AllowedCurrency), + ("balance", account.Balance))); } private void OnTerminalMessage(EntityUid uid, EconomyBankTerminalComponent comp, EconomyTerminalMessage args) @@ -110,153 +88,20 @@ private void OnATMItemSlotChanged(EntityUid uid, EconomyBankATMComponent atm, Co UpdateATMUserInterface((uid, atm)); } - private void OnATMWithdrawMessage(EntityUid uid, EconomyBankATMComponent atm, EconomyBankATMWithdrawMessage args) - { - var bankAccount = GetATMInsertedAccount(atm); - if (bankAccount is null) - return; - - string? error; - - TryWithdraw(bankAccount, atm, args.Amount, out error); - UpdateATMUserInterface((uid, atm), error); - } - - private void OnATMTransferMessage(EntityUid uid, EconomyBankATMComponent atm, EconomyBankATMTransferMessage args) - { - var bankAccount = GetATMInsertedAccount(atm); - if (bankAccount is null) - return; - - string? error; - - TrySendMoney(bankAccount, args.RecipientAccountId, args.Amount, out error); - UpdateATMUserInterface((uid, atm), error); - } - - [PublicAPI] - public EconomyBankAccountComponent? FindAccountById(string id) - { - var accounts = GetAccounts(); - if (accounts.TryGetValue(id, out var comp)) - return comp; - - return null; - } - - private void Withdraw(EconomyBankAccountComponent component, EconomyBankATMComponent atm, ulong sum) - { - component.Balance -= sum; - var pos = Comp(atm.Owner).MapPosition; - DropMoneyHandler(component.MoneyHolderEntId, sum, pos); - - component.Logs.Add(new(_gameTiming.CurTime, Loc.GetString("economybanksystem-log-withdraw", - ("amount", sum), ("currencyName", component.AllowCurrency)))); - - _entManager.Dirty(component); - } - - [PublicAPI] - public bool TryWithdraw(EconomyBankAccountComponent component, EconomyBankATMComponent atm, ulong sum, [NotNullWhen(false)] out string? errorMessage) - { - errorMessage = ""; - if (sum > 0 && component.Balance >= sum) - { - Withdraw(component, atm, sum); - return true; - } - errorMessage = Loc.GetString("economybanksystem-transaction-error-notenoughmoney"); - return false; - } - - [PublicAPI] - public Entity DropMoneyHandler(EntProtoId entId, ulong amount, MapCoordinates pos) - { - var ent = Spawn(entId, pos); - - var moneyHolderComp = Comp(ent); - moneyHolderComp.Balance = amount; - - _entManager.Dirty(moneyHolderComp); - - return (ent, moneyHolderComp); - } - - private void SendMoney(IEconomyMoneyHolder fromAccount, EconomyBankAccountComponent toSend, ulong amount) - { - fromAccount.Balance -= amount; - toSend.Balance += amount; - - string senderAccoutId = "UNEXPECTED"; - if (fromAccount is EconomyBankAccountComponent) - { - var fromAccountComponent = (fromAccount as EconomyBankAccountComponent)!; - fromAccountComponent.Logs.Add(new(_gameTiming.CurTime, Loc.GetString("economybanksystem-log-send-to", - ("amount", amount), ("currencyName", toSend.AllowCurrency), ("accountId", toSend.AccountId)))); - - senderAccoutId = fromAccountComponent.AccountId; - } - toSend.Logs.Add(new(_gameTiming.CurTime, Loc.GetString("economybanksystem-log-send-from", - ("amount", amount), ("currencyName", toSend.AllowCurrency), ("accountId", senderAccoutId)))); - - _entManager.Dirty((fromAccount as Component)!); - _entManager.Dirty(toSend); - } - -/* TODO: - public void AddLog(EconomyBankAccountComponent comp, )*/ - - [PublicAPI] - public bool TrySendMoney(IEconomyMoneyHolder fromAccount, EconomyBankAccountComponent? recipientAccount, ulong amount, [NotNullWhen(false)] out string? errorMessage) - { - errorMessage = null; - - if (fromAccount.Balance >= amount) - { - if (recipientAccount is not null) - { - if (fromAccount == recipientAccount) - { - errorMessage = "407"; - return false; - } - SendMoney(fromAccount, recipientAccount, amount); - return true; - } - - errorMessage = Loc.GetString("economybanksystem-transaction-error-notfoundaccout"); - return false; - } - - errorMessage = Loc.GetString("economybanksystem-transaction-error-notenoughmoney"); - return false; - } - - [PublicAPI] - public bool TrySendMoney(IEconomyMoneyHolder fromAccount, string recipientAccountId, ulong amount, [NotNullWhen(false)] out string? errorMessage) - { - errorMessage = null; - - var recipientAccount = FindAccountById(recipientAccountId); - if (recipientAccount is null) - { - errorMessage = Loc.GetString("economybanksystem-transaction-error-notfoundaccout", ("accountId", recipientAccountId)); - return false; - } - - return TrySendMoney(fromAccount, recipientAccount, amount, out errorMessage); - } - [PublicAPI] public void UpdateATMUserInterface(Entity entity, string? error = null) { - var bankAccount = GetATMInsertedAccount(entity.Comp); + var card = GetATMInsertedAccount(entity.Comp); + EconomyBankAccount? bankAccount = null; + if (card is not null && _economyManager.TryGetAccount(card.AccountID, out var account)) + bankAccount = account; + _userInterfaceSystem.SetUiState(entity.Owner, EconomyBankATMUiKey.Key, new EconomyBankATMUserInterfaceState() { BankAccount = bankAccount is null ? null : new() { Balance = bankAccount.Balance, - AccountId = bankAccount.AccountId, + AccountId = bankAccount.AccountID, AccountName = bankAccount.AccountName, Blocked = bankAccount.Blocked, }, @@ -282,39 +127,11 @@ public void UpdateTerminal(Entity entity, ulong am _entManager.Dirty(entity); } - - [PublicAPI] - public Dictionary GetAccounts(EconomyBankAccountMask flag = EconomyBankAccountMask.Activated) - { - Dictionary list = new(); - - var accountsEnum = AllEntityQuery(); - while (accountsEnum.MoveNext(out var comp)) - { - switch (flag) - { - case EconomyBankAccountMask.Activated: - if (comp.Activated) - list.Add(comp.AccountId, comp); - break; - case EconomyBankAccountMask.ActivatedBlocked: - if (comp.Activated && comp.Blocked) - list.Add(comp.AccountId, comp); - break; - case EconomyBankAccountMask.ActivatedNotBlocked: - if (comp.Activated && !comp.Blocked) - list.Add(comp.AccountId, comp); - break; - } - } - - return list; - } } public enum EconomyBankAccountMask { - Activated, - ActivatedBlocked, - ActivatedNotBlocked, + All, + NotBlocked, + Blocked, } } diff --git a/Content.Shared/AWS/Economy/IEconomyMoneyHolder.cs b/Content.Shared/AWS/Economy/IEconomyMoneyHolder.cs index ecec51ca39e..3e706ce980c 100644 --- a/Content.Shared/AWS/Economy/IEconomyMoneyHolder.cs +++ b/Content.Shared/AWS/Economy/IEconomyMoneyHolder.cs @@ -1,5 +1,8 @@ namespace Content.Shared.AWS.Economy { + /// + /// Interface for components that hold money. Not to ones that can carry a link to the account (like a card). + /// public interface IEconomyMoneyHolder { public ulong Balance { get; set; } diff --git a/Content.Shared/AWS/Economy/ISharedEconomyManager.cs b/Content.Shared/AWS/Economy/ISharedEconomyManager.cs new file mode 100644 index 00000000000..4adcc8e2fa3 --- /dev/null +++ b/Content.Shared/AWS/Economy/ISharedEconomyManager.cs @@ -0,0 +1,26 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Content.Shared.AWS.Economy; + +public interface ISharedEconomyManager +{ + /// + /// Checks if the account exists (valid). + /// + /// True if the account exists, false otherwise. + bool IsValidAccount(string accountID); + + /// + /// Tries to get the account with the given ID. + /// + /// True if the fetching was successful, false otherwise. + bool TryGetAccount(string accountID, [NotNullWhen(true)] out EconomyBankAccount? account); + + /// + /// Returns all currently existing accounts. + /// + /// Filter mask to fetch accounts. + IReadOnlyDictionary GetAccounts(EconomyBankAccountMask flag = EconomyBankAccountMask.NotBlocked); + + void Initialize(); +} \ No newline at end of file diff --git a/Content.Shared/AWS/Economy/MsgEconomyAccountList.cs b/Content.Shared/AWS/Economy/MsgEconomyAccountList.cs new file mode 100644 index 00000000000..c0c02f49414 --- /dev/null +++ b/Content.Shared/AWS/Economy/MsgEconomyAccountList.cs @@ -0,0 +1,81 @@ +using System.Collections.Frozen; +using Lidgren.Network; +using Robust.Shared.Network; +using Robust.Shared.Serialization; + +namespace Content.Shared.AWS.Economy; + +public sealed class MsgEconomyAccountList : NetMessage +{ + public override MsgGroups MsgGroup => MsgGroups.Command; + + public FrozenDictionary Accounts = default!; + + public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) + { + var dictionaryCount = buffer.ReadByte(); + buffer.ReadPadBits(); + + Dictionary accounts = new(); + for (var i = 0; i < dictionaryCount; i++) + { + var key = buffer.ReadString(); + + var id = buffer.ReadString(); + var name = buffer.ReadString(); + var currency = buffer.ReadString(); + var balance = buffer.ReadUInt64(); + var penalty = buffer.ReadUInt64(); + var blocked = buffer.ReadBoolean(); + var canReachPayday = buffer.ReadBoolean(); + + // read logs + var logsCount = buffer.ReadByte(); + buffer.ReadPadBits(); + List logs = new(); + for (var j = 0; j < logsCount; j++) + { + var date = TimeSpan.FromTicks(buffer.ReadInt64()); + var text = buffer.ReadString(); + logs.Add(new EconomyBankAccountLogField(date, text)); + } + + // add entry to the dictionary + accounts.Add(key, new EconomyBankAccount(id, name, currency, balance, penalty, blocked, canReachPayday, logs)); + } + + Accounts = accounts.ToFrozenDictionary(); + } + + public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) + { + // This shit is fucking nuts + buffer.Write((byte)Accounts.Count); + buffer.WritePadBits(); + + foreach (var (key, value) in Accounts) + { + buffer.Write(key); + + buffer.Write(value.AccountID); + buffer.Write(value.AccountName); + buffer.Write(value.AllowedCurrency); + buffer.Write(value.Balance); + buffer.Write(value.Penalty); + buffer.Write(value.Blocked); + buffer.Write(value.CanReachPayDay); + + // write logs + var logs = value.Logs; + buffer.Write((byte)logs.Count); + buffer.WritePadBits(); + foreach (var log in logs) + { + buffer.Write(log.Date.Ticks); + buffer.Write(log.Text); + } + } + } + + public override NetDeliveryMethod DeliveryMethod => NetDeliveryMethod.ReliableOrdered; +} \ No newline at end of file diff --git a/Content.Shared/AWS/Economy/MsgEconomyAccountListRequest.cs b/Content.Shared/AWS/Economy/MsgEconomyAccountListRequest.cs new file mode 100644 index 00000000000..7b61854834e --- /dev/null +++ b/Content.Shared/AWS/Economy/MsgEconomyAccountListRequest.cs @@ -0,0 +1,22 @@ +using Lidgren.Network; +using Robust.Shared.Network; +using Robust.Shared.Serialization; + +namespace Content.Shared.AWS.Economy; + +public sealed class MsgEconomyAccountListRequest : NetMessage +{ + public override MsgGroups MsgGroup => MsgGroups.Command; + + public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) + { + + } + + public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) + { + + } + + public override NetDeliveryMethod DeliveryMethod => NetDeliveryMethod.ReliableUnordered; +} \ No newline at end of file diff --git a/Resources/Locale/ru-RU/aws/economy/logs.ftl b/Resources/Locale/ru-RU/aws/economy/logs.ftl index 2db982e459a..bd5b9418de6 100644 --- a/Resources/Locale/ru-RU/aws/economy/logs.ftl +++ b/Resources/Locale/ru-RU/aws/economy/logs.ftl @@ -1,4 +1,5 @@ economybanksystem-log-withdraw = Снятие {$amount} {$currencyName} +economybanksystem-log-insert = Пополнение на {$amount} {$currencyName} economybanksystem-log-send-to = Перевод {$amount} {$currencyName} на {$accountId} economybanksystem-log-send-from = Пополнение на {$amount} {$currencyName} с {$accountId} economybanksystem-log-vending-buying = Покупка {$itemName} diff --git a/Resources/Prototypes/AWS/Economy/nanotrasen_wallet.yml b/Resources/Prototypes/AWS/Economy/nanotrasen_wallet.yml index 7313b58def1..2e5ebe2d65f 100644 --- a/Resources/Prototypes/AWS/Economy/nanotrasen_wallet.yml +++ b/Resources/Prototypes/AWS/Economy/nanotrasen_wallet.yml @@ -63,8 +63,8 @@ id: Nanotrasen descriptior: "-" prefix: "NT" - strik: 1 - numbersPerStrik: 6 + streak: 1 + numbersPerStreak: 6 - type: entity parent: BaseItem @@ -82,10 +82,12 @@ heldPrefix: default storedRotation: -90 - type: EconomyBankAccount - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 10000 + accountSetup: + generateAccountID: true + allowedCurrency: Thaler + balance: 10000 # - type: entity # parent: IDCardStandard @@ -105,13 +107,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Command - allowCurrency: Thaler - accountName: Станция accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 10000 - activateOnSpawn: true + accountSetup: + accountID: NT-Command + accountName: Станция + allowedCurrency: Thaler + balance: 10000 - type: entity parent: BankAccountNanotrasen @@ -120,13 +122,14 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Engineering - allowCurrency: Thaler - accountName: Инженерный отдел accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 1000 - activateOnSpawn: true + accountSetup: + accountID: NT-Engineering + accountName: Инженерный отдел + allowedCurrency: Thaler + balance: 1000 + - type: entity parent: BankAccountNanotrasen @@ -135,13 +138,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Service - allowCurrency: Thaler - accountName: Сервисный отдел accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 500 - activateOnSpawn: true + accountSetup: + accountID: NT-Service + accountName: Сервисный отдел + allowedCurrency: Thaler + balance: 500 - type: entity parent: BankAccountNanotrasen @@ -150,13 +153,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Security - allowCurrency: Thaler - accountName: Служба безопасности accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 1000 - activateOnSpawn: true + accountSetup: + accountID: NT-Security + allowedCurrency: Thaler + accountName: Служба безопасности + balance: 1000 - type: entity parent: BankAccountNanotrasen @@ -165,13 +168,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Medical - allowCurrency: Thaler - accountName: Медицинский отдел accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 900 - activateOnSpawn: true + accountSetup: + accountID: NT-Medical + accountName: Медицинский отдел + allowedCurrency: Thaler + balance: 900 - type: entity parent: BankAccountNanotrasen @@ -180,13 +183,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Science - allowCurrency: Thaler - accountName: Научный отдел accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 1000 - activateOnSpawn: true + accountSetup: + accountID: NT-Science + accountName: Научный отдел + allowedCurrency: Thaler + balance: 1000 - type: entity parent: BankAccountNanotrasen @@ -195,13 +198,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Cargo - allowCurrency: Thaler - accountName: Карго отдел accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 1000 - activateOnSpawn: true + accountSetup: + accountID: NT-Cargo + accountName: Карго отдел + allowedCurrency: Thaler + balance: 1000 - type: entity parent: BankAccountNanotrasen @@ -211,13 +214,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-CentCom - allowCurrency: Thaler - accountName: Ценком accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 18446744073709524615 - activateOnSpawn: true + accountSetup: + accountID: NT-CentCom + accountName: Центком + allowedCurrency: Thaler + balance: 18446744073709524615 #Кассы - type: entity @@ -228,13 +231,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Service-B - allowCurrency: Thaler - accountName: Касса Бара accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 250 - activateOnSpawn: true + accountSetup: + accountID: NT-Service-B + accountName: Касса Бара + allowedCurrency: Thaler + balance: 250 - type: entity parent: BankAccountNanotrasen @@ -244,13 +247,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Service-K - allowCurrency: Thaler - accountName: Касса Кухни accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 125 - activateOnSpawn: true + accountSetup: + accountID: NT-Service-K + accountName: Касса Кухни + allowedCurrency: Thaler + balance: 125 - type: entity parent: BankAccountNanotrasen @@ -260,13 +263,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Service-H - allowCurrency: Thaler - accountName: Касса Гидропоники accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 125 - activateOnSpawn: true + accountSetup: + accountID: NT-Service-H + accountName: Касса Гидропоники + allowedCurrency: Thaler + balance: 125 - type: entity parent: BankAccountNanotrasen @@ -276,13 +279,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Medical-C - allowCurrency: Thaler - accountName: Касса Химии accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 100 - activateOnSpawn: true + accountSetup: + accountID: NT-Medical-C + accountName: Касса Химии + allowedCurrency: Thaler + balance: 100 - type: entity parent: BankAccountNanotrasen @@ -292,13 +295,13 @@ suffix: Economy components: - type: EconomyBankAccount - accountId: NT-Medical-M - allowCurrency: Thaler - accountName: Касса Меда accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder - balance: 50 - activateOnSpawn: true + accountSetup: + accountID: NT-Medical-M + accountName: Касса Меда + allowedCurrency: Thaler + balance: 50 #Сейфы - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml index 88f22982558..afd02c5bfbb 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml @@ -39,9 +39,10 @@ - type: PresetIdCard job: Passenger - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: PassengerIDCard @@ -51,9 +52,10 @@ - type: PresetIdCard job: TechnicalAssistant - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: Sprite layers: - state: default @@ -67,9 +69,10 @@ - type: PresetIdCard job: MedicalIntern - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: Sprite layers: - state: default @@ -83,9 +86,10 @@ - type: PresetIdCard job: ResearchAssistant - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: Sprite layers: - state: default @@ -99,9 +103,10 @@ - type: PresetIdCard job: SecurityCadet - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: Sprite layers: - state: default @@ -115,9 +120,10 @@ - type: PresetIdCard job: ServiceWorker - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: Sprite layers: - state: default @@ -144,9 +150,10 @@ - type: StealTarget stealGroup: CaptainIDCard - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -160,25 +167,27 @@ - type: PresetIdCard job: SecurityOfficer - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard id: WardenIDCard name: warden ID card components: - - type: Sprite - layers: - - state: default - - state: idwarden - - type: PresetIdCard - job: Warden - - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler - accountIdByProto: Nanotrasen - moneyHolderEntId: ThalerHolder + - type: Sprite + layers: + - state: default + - state: idwarden + - type: PresetIdCard + job: Warden + - type: EconomyBankAccount #SS14RU + accountIdByProto: Nanotrasen + moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -192,9 +201,10 @@ - type: PresetIdCard job: StationEngineer - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -208,9 +218,10 @@ - type: PresetIdCard job: MedicalDoctor - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -224,9 +235,10 @@ - type: PresetIdCard job: Paramedic - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -240,9 +252,10 @@ - type: PresetIdCard job: Chemist - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -256,9 +269,10 @@ - type: PresetIdCard job: CargoTechnician - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -272,27 +286,29 @@ - type: PresetIdCard job: SalvageSpecialist - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard id: QuartermasterIDCard name: quartermaster ID card components: - - type: Sprite - layers: - - state: silver - - state: idquartermaster - - type: Item - heldPrefix: silver - - type: PresetIdCard - job: Quartermaster - - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler - accountIdByProto: Nanotrasen - moneyHolderEntId: ThalerHolder + - type: Sprite + layers: + - state: silver + - state: idquartermaster + - type: Item + heldPrefix: silver + - type: PresetIdCard + job: Quartermaster + - type: EconomyBankAccount #SS14RU + accountIdByProto: Nanotrasen + moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -306,9 +322,10 @@ - type: PresetIdCard job: Scientist - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -322,9 +339,10 @@ - type: PresetIdCard job: Clown - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -338,25 +356,27 @@ - type: PresetIdCard job: Mime - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard id: ChaplainIDCard name: chaplain ID card components: - - type: Sprite - layers: - - state: default - - state: idchaplain - - type: PresetIdCard - job: Chaplain - - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler - accountIdByProto: Nanotrasen - moneyHolderEntId: ThalerHolder + - type: Sprite + layers: + - state: default + - state: idchaplain + - type: PresetIdCard + job: Chaplain + - type: EconomyBankAccount #SS14RU + accountIdByProto: Nanotrasen + moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -370,9 +390,10 @@ - type: PresetIdCard job: Janitor - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -386,26 +407,28 @@ - type: PresetIdCard job: Bartender - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard id: PunPunIDCard name: pun pun ID card components: - - type: Sprite - layers: - - state: default - - state: idbartender - - type: PresetIdCard - job: Bartender - name: Pun Pun - - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler - accountIdByProto: Nanotrasen - moneyHolderEntId: ThalerHolder + - type: Sprite + layers: + - state: default + - state: idbartender + - type: PresetIdCard + job: Bartender + name: Pun Pun + - type: EconomyBankAccount #SS14RU + accountIdByProto: Nanotrasen + moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -419,9 +442,10 @@ - type: PresetIdCard job: Chef - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -435,9 +459,10 @@ - type: PresetIdCard job: Botanist - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -451,9 +476,10 @@ - type: PresetIdCard job: Librarian - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -467,9 +493,10 @@ - type: PresetIdCard job: Lawyer - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -485,9 +512,10 @@ - type: PresetIdCard job: HeadOfPersonnel - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -503,9 +531,10 @@ - type: PresetIdCard job: ChiefEngineer - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -521,9 +550,10 @@ - type: PresetIdCard job: ChiefMedicalOfficer - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -539,9 +569,10 @@ - type: PresetIdCard job: ResearchDirector - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -557,9 +588,10 @@ - type: PresetIdCard job: HeadOfSecurity - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -576,9 +608,10 @@ - type: PresetIdCard job: Visitor - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -590,9 +623,10 @@ - state: default - state: idbrigmedic - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -608,9 +642,10 @@ - type: PresetIdCard job: CentralCommandOfficial - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -626,9 +661,10 @@ - type: Item heldPrefix: gold - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -644,9 +680,10 @@ - type: Item heldPrefix: blue - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -660,9 +697,10 @@ - type: PresetIdCard job: ERTEngineer - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -676,9 +714,10 @@ - type: PresetIdCard job: ERTJanitor - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -692,9 +731,10 @@ - type: PresetIdCard job: ERTMedical - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -708,9 +748,10 @@ - type: PresetIdCard job: ERTSecurity - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -724,9 +765,10 @@ - type: PresetIdCard job: Musician - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -741,9 +783,10 @@ - type: PresetIdCard job: DeathSquad - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity name: passenger ID card @@ -817,9 +860,10 @@ - type: PresetIdCard job: AtmosphericTechnician - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -859,9 +903,10 @@ - type: PresetIdCard job: Psychologist - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -875,9 +920,10 @@ - type: PresetIdCard job: Reporter - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -891,9 +937,10 @@ - type: PresetIdCard job: Boxer - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -907,9 +954,10 @@ - type: PresetIdCard job: Zookeeper - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard @@ -923,9 +971,10 @@ - type: PresetIdCard job: Detective - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: CentcomIDCard @@ -941,9 +990,10 @@ - type: PresetIdCard job: CBURN - type: EconomyBankAccount #SS14RU - allowCurrency: Thaler accountIdByProto: Nanotrasen moneyHolderEntId: ThalerHolder + accountSetup: + allowedCurrency: Thaler - type: entity parent: IDCardStandard