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.CartridgeLoader; using Content.Shared.GameTicking; using Content.Shared.Inventory; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Mobs.Systems; using Content.Shared._White.Economy; using Robust.Server.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server._White.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; } }