* nasrano

* Add station budget & salary

* Initial balance

* Vending machine UI

* Pricing for vending machines

* Finish economy

* Eftpos

* Finish eftpos

* Put eftpos into lockers

* Vending machine prices

* Small fixes

* Fix atm UI

* Add atms to maps

* fix

* reset

* Add PDA program

* Maps again

---------

Co-authored-by: Mona Hmiza <>
Co-authored-by: rhailrake <49613070+rhailrake@users.noreply.github.com>
This commit is contained in:
Aviu00
2023-10-02 16:50:02 +09:00
committed by Aviu00
parent 707cdf3afa
commit 16c8b95da4
109 changed files with 2043 additions and 30 deletions

View File

@@ -32,7 +32,7 @@ public sealed class CharacterInfoSystem : EntitySystem
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);
OnCharacterUpdate?.Invoke(data);
}
@@ -49,7 +49,8 @@ public sealed class CharacterInfoSystem : EntitySystem
string Job,
Dictionary<string, List<ObjectiveInfo>> Objectives,
string? Briefing,
string EntityName
string EntityName,
Dictionary<string, string> Memory
);
/// <summary>

View File

@@ -105,12 +105,13 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
return;
}
var (entity, job, objectives, briefing, entityName) = data;
var (entity, job, objectives, briefing, entityName, memories) = data;
_window.SpriteView.SetEntity(entity);
_window.NameLabel.Text = entityName;
_window.SubText.Text = job;
_window.Objectives.RemoveAllChildren();
_window.Memories.RemoveAllChildren();
_window.ObjectivesLabel.Visible = objectives.Any();
foreach (var (groupId, conditions) in objectives)
@@ -146,6 +147,26 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
_window.Objectives.AddChild(objectiveControl);
}
//WD EDIT
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);
}
//WD EDIT END
if (briefing != null)
{
var briefingControl = new ObjectiveBriefingControl();

View File

@@ -14,6 +14,13 @@
<Label Name="SubText" VerticalAlignment="Top" StyleClasses="LabelSubText" Access="Public"/>
</BoxContainer>
</BoxContainer>
<!-- WD EDIT -->
<Label Text="{Loc 'character-info-memories-label'}" HorizontalAlignment="Center"/>
<BoxContainer Orientation="Vertical" Name="Memories" Access="Public"/>
<cc:Placeholder PlaceholderText="{Loc 'character-info-memories-placeholder-text'}"/>
<!-- WD EDIT END -->
<Label Name="ObjectivesLabel" Access="Public" Text="{Loc 'character-info-objectives-label'}" HorizontalAlignment="Center"/>
<BoxContainer Orientation="Vertical" Name="Objectives" Access="Public"/>
<cc:Placeholder Name="RolePlaceholder" Access="Public" PlaceholderText="{Loc 'character-info-roles-antagonist-text'}"/>

View File

@@ -2,13 +2,14 @@ using Content.Client.VendingMachines.UI;
using Content.Shared.VendingMachines;
using Robust.Client.UserInterface.Controls;
using System.Linq;
using Content.Client.White.Economy.Ui;
namespace Content.Client.VendingMachines
{
public sealed class VendingMachineBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private VendingMachineMenu? _menu;
private VendingMenu? _menu; // WD EDIT
[ViewVariables]
private List<VendingMachineInventoryEntry> _cachedInventory = new();
@@ -26,15 +27,18 @@ namespace Content.Client.VendingMachines
var vendingMachineSys = EntMan.System<VendingMachineSystem>();
_cachedInventory = vendingMachineSys.GetAllInventory(Owner);
// WD EDIT START
var component = EntMan.GetComponent<VendingMachineComponent>(Owner);
_cachedInventory = vendingMachineSys.GetAllInventory(Owner, component);
_menu = new VendingMachineMenu { Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName };
_menu = new VendingMenu { Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName };
_menu.OnClose += Close;
_menu.OnItemSelected += OnItemSelected;
_menu.OnSearchChanged += OnSearchChanged;
_menu.OnWithdraw += SendMessage;
// WD EDIT END
_menu.Populate(_cachedInventory, out _cachedFilteredIndex);
_menu.Populate(_cachedInventory, component.PriceMultiplier, component.Credits);
_menu.OpenCentered();
}
@@ -48,21 +52,23 @@ namespace Content.Client.VendingMachines
_cachedInventory = newState.Inventory;
_menu?.Populate(_cachedInventory, out _cachedFilteredIndex, _menu.SearchBar.Text);
_menu?.Populate(_cachedInventory, newState.PriceMultiplier, newState.Credits);
}
private void OnItemSelected(ItemList.ItemListSelectedEventArgs args)
// WD EDIT START
private void OnItemSelected(int index)
{
if (_cachedInventory.Count == 0)
return;
var selectedItem = _cachedInventory.ElementAtOrDefault(_cachedFilteredIndex.ElementAtOrDefault(args.ItemIndex));
var selectedItem = _cachedInventory.ElementAtOrDefault(index);
if (selectedItem == null)
return;
SendMessage(new VendingMachineEjectMessage(selectedItem.Type, selectedItem.ID));
}
// WD EDIT END
protected override void Dispose(bool disposing)
{
@@ -80,7 +86,7 @@ namespace Content.Client.VendingMachines
private void OnSearchChanged(string? filter)
{
_menu?.Populate(_cachedInventory, out _cachedFilteredIndex, filter);
//_menu?.Populate(_cachedInventory, out _cachedFilteredIndex, filter);
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Content.Client.White.Economy;
public enum ATMVisualLayers : byte
{
Base,
BaseUnshaded
}

View File

@@ -0,0 +1,43 @@
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.White.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);
}
}

View File

@@ -0,0 +1,20 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="ATM"
SetSize="400 220"
Resizable="False">
<BoxContainer Name="AuthorizationPage" HorizontalExpand="True" VerticalExpand="True">
<BoxContainer HorizontalAlignment="Center" HorizontalExpand="True" VerticalAlignment="Center" Orientation="Vertical" MinSize="325 160">
<Label Text="NANOTRASEN ATM"/>
<Label Name="BalanceLabel" Visible="False"/>
<controls:HighDivider Name="Divider" Visible="False"/>
<Label Name="StatusLabel"/>
<SliderIntInput Name="WithdrawSlider"/>
<BoxContainer HorizontalExpand="True" VerticalExpand="True" VerticalAlignment="Bottom" Orientation="Horizontal">
<LineEdit Name="PinLineEdit" HorizontalExpand="True" Text="Enter PIN"/>
<Button Name="WithdrawButton" Text="{Loc 'store-ui-default-withdraw-text'}"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,79 @@
using System.Text.RegularExpressions;
using Content.Shared.White.Economy;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
namespace Content.Client.White.Economy.Ui;
[GenerateTypedNameReferences]
public sealed partial class AtmWindow : DefaultWindow
{
public Action<ATMRequestWithdrawMessage>? 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(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;
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;
Divider.Visible = true;
WithdrawSlider.Visible = true;
PinLineEdit.Visible = true;
WithdrawButton.Visible = true;
WithdrawSlider.MaxValue = cast.AccountBalance;
WithdrawSlider.Value = Math.Min(WithdrawSlider.Value, cast.AccountBalance);
}
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;
}
}

View File

@@ -0,0 +1,29 @@
using Content.Client.UserInterface.Fragments;
using Content.Shared.White.Economy;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
namespace Content.Client.White.Economy.Ui;
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();
}
public override void UpdateState(BoundUserInterfaceState state)
{
if (state is not BankCartridgeUiState bankState)
return;
_fragment?.UpdateState(bankState);
}
}

View File

@@ -0,0 +1,12 @@
<ui:BankUiFragment xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.White.Economy.Ui">
<PanelContainer StyleClasses="BackgroundDark"></PanelContainer>
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="8">
<BoxContainer Name="LinkedAccount" Orientation="Vertical" Visible="False">
<RichTextLabel Name="LinkedAccountNameLabel"/>
<RichTextLabel Name="LinkedAccountBalanceLabel"/>
</BoxContainer>
<RichTextLabel Name="NoLinkedAccountLabel" Visible="False"/>
</BoxContainer>
</ui:BankUiFragment>

View File

@@ -0,0 +1,30 @@
using Content.Client.Message;
using Content.Shared.White.Economy;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.White.Economy.Ui;
[GenerateTypedNameReferences]
public sealed partial class BankUiFragment : BoxContainer
{
public BankUiFragment()
{
RobustXamlLoader.Load(this);
}
public void UpdateState(BankCartridgeUiState state)
{
LinkedAccount.Visible = state.AccountLinked;
NoLinkedAccountLabel.Visible = !state.AccountLinked;
if (state.AccountLinked)
{
LinkedAccountNameLabel.SetMarkup(Loc.GetString("eftpos-ui-account-text", ("owner", state.OwnerName)));
LinkedAccountBalanceLabel.SetMarkup(Loc.GetString("atm-ui-balance", ("balance", state.Balance)));
return;
}
NoLinkedAccountLabel.SetMarkup(Loc.GetString("bank-program-ui-no-account"));
}
}

View File

@@ -0,0 +1,41 @@
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.White.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);
}
}

View File

@@ -0,0 +1,30 @@
<DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc 'eftpos-ui-title'}"
Resizable="False"
SetSize="380 120">
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label
Name="AmountLabel"
Text="{Loc 'eftpos-ui-amount-text'}" />
<LineEdit
Name="AmountLineEdit"
PlaceHolder="0"
MinWidth="60"
Access="Public" />
<Button
Name="CardButton"
Access="Public"
HorizontalExpand="True"
HorizontalAlignment="Right"
ToolTip="{Loc 'eftpos-ui-card-lock-desc'}"
Text="{Loc 'eftpos-ui-card-lock-text'}" />
</BoxContainer>
<Label
Name="AccountLabel"
VerticalExpand="True"
VerticalAlignment="Bottom"
Access="Public" />
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,58 @@
using Content.Shared.White.Economy;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.White.Economy.Ui;
[GenerateTypedNameReferences]
public sealed partial class EftposWindow : DefaultWindow
{
public Action<EftposLockMessage>? OnCardButtonPressed;
public EftposWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
AmountLineEdit.OnTextChanged += _ =>
{
if (!int.TryParse(AmountLineEdit.Text, out var result) || result <= 0)
AmountLineEdit.Text = string.Empty;
AmountLineEdit.Text = result.ToString();
};
CardButton.OnPressed += _ =>
{
if (!int.TryParse(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));
}
}

View File

@@ -0,0 +1,13 @@
<Control xmlns="https://spacestation14.io">
<BoxContainer Orientation="Horizontal">
<TextureRect
Name="VendingItemTexture"
Stretch="KeepAspectCentered" />
<Label Name="VendingItemName" HorizontalExpand="True" />
<Button
Name="VendingItemBuyButton"
MinWidth="80"
HorizontalAlignment="Right"
Access="Public" />
</BoxContainer>
</Control>

View File

@@ -0,0 +1,22 @@
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.White.Economy.Ui;
[GenerateTypedNameReferences]
public sealed partial class VendingItem : Control
{
public VendingItem(string itemName, string price, Texture? texture = null)
{
RobustXamlLoader.Load(this);
VendingItemName.Text = itemName;
VendingItemBuyButton.Text = price;
// VendingItemBuyButton.Disabled = !canBuy;
VendingItemTexture.Texture = texture;
}
}

View File

@@ -0,0 +1,26 @@
<DefaultWindow xmlns="https://spacestation14.io">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<Label Name="CreditsLabel" HorizontalExpand="True"/>
<Button
Name="WithdrawButton"
Text="{Loc 'store-ui-default-withdraw-text'}"
HorizontalAlignment="Right"/>
</BoxContainer>
<PanelContainer StyleClasses="HighDivider" />
<Label
Name="OutOfStockLabel"
Visible="False"
Text="{Loc 'vending-machine-component-try-eject-out-of-stock'}"/>
<ScrollContainer
HScrollEnabled="False"
HorizontalExpand="True"
VerticalExpand="True">
<BoxContainer
Name="VendingContents"
Orientation="Vertical"
VerticalExpand="True">
</BoxContainer>
</ScrollContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,88 @@
using System.Numerics;
using Content.Shared.VendingMachines;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.White.Economy.Ui;
[GenerateTypedNameReferences]
public sealed partial class VendingMenu : DefaultWindow
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public event Action<int>? OnItemSelected;
public Action<VendingMachineWithdrawMessage>? OnWithdraw;
public VendingMenu()
{
MinSize = SetSize = new Vector2(250, 150);
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}
/// <summary>
/// Populates the list of available items on the vending machine interface
/// and sets icons based on their prototypes
/// </summary>
public void Populate(List<VendingMachineInventoryEntry> inventory, double priceMultiplier, int credits)
{
CreditsLabel.Text = Loc.GetString("vending-ui-credits-amount", ("credits", credits));
WithdrawButton.Disabled = credits == 0;
WithdrawButton.OnPressed += _ =>
{
if (credits == 0)
return;
OnWithdraw?.Invoke(new VendingMachineWithdrawMessage());
};
VendingContents.RemoveAllChildren();
if (inventory.Count == 0)
{
OutOfStockLabel.Visible = true;
SetSizeAfterUpdate(OutOfStockLabel.Text?.Length ?? 0);
return;
}
OutOfStockLabel.Visible = false;
var longestEntry = string.Empty;
var spriteSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SpriteSystem>();
for (var i = 0; i < inventory.Count; i++)
{
var entry = inventory[i];
var itemName = entry.ID;
Texture? icon = null;
if (_prototypeManager.TryIndex<EntityPrototype>(entry.ID, out var prototype))
{
itemName = prototype.Name;
icon = spriteSystem.GetPrototypeIcon(prototype).Default;
}
if (itemName.Length > longestEntry.Length)
longestEntry = itemName;
var price = (int) (entry.Price * priceMultiplier);
var vendingItem = new VendingItem($"{itemName} [{entry.Amount}]", $"{price} ¢", icon);
var j = i;
vendingItem.VendingItemBuyButton.OnPressed += _ =>
{
OnItemSelected?.Invoke(j);
};
VendingContents.AddChild(vendingItem);
}
SetSizeAfterUpdate(longestEntry.Length);
}
private void SetSizeAfterUpdate(int longestEntryLength)
{
SetSize = new Vector2(Math.Clamp((longestEntryLength + 10) * 12, 250, 700),
Math.Clamp(VendingContents.ChildCount * 50, 150, 400));
}
}

View File

@@ -1,3 +1,4 @@
using Content.Server.White.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;
// WD EDIT START
[ViewVariables(VVAccess.ReadWrite)]
public int Balance
{
get => BankAccount.Balance;
set => BankAccount.Balance = value;
}
[ViewVariables]
public BankAccount BankAccount = default!;
// WD EDIT END
/// <summary>
/// How much the bank balance goes up per second, every Delay period. Rounded down when multiplied.

View File

@@ -6,6 +6,7 @@ using Content.Server.Popups;
using Content.Server.Shuttles.Systems;
using Content.Server.Stack;
using Content.Server.Station.Systems;
using Content.Server.White.Economy;
using Content.Shared.Access.Systems;
using Content.Shared.Administration.Logs;
using Content.Shared.Cargo;
@@ -46,6 +47,7 @@ public sealed partial class CargoSystem : SharedCargoSystem
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
[Dependency] private readonly BankCardSystem _bankCard = default!; // WD
private EntityQuery<TransformComponent> _xformQuery;
private EntityQuery<CargoSellBlacklistComponent> _blacklistQuery;
@@ -63,6 +65,15 @@ public sealed partial class CargoSystem : SharedCargoSystem
InitializeShuttle();
InitializeTelepad();
InitializeBounty();
SubscribeLocalEvent<StationBankAccountComponent, ComponentInit>(OnInit); // WD
}
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");
}
public override void Shutdown()

View File

@@ -32,6 +32,7 @@ public sealed class CharacterInfoSystem : EntitySystem
var objectives = new Dictionary<string, List<ObjectiveInfo>>();
var jobTitle = "No Profession";
var memories = new Dictionary<string, string>(); //WD EDIT
string? briefing = null;
if (_minds.TryGetMind(entity, out var mindId, out var mind))
{
@@ -54,8 +55,14 @@ public sealed class CharacterInfoSystem : EntitySystem
// Get briefing
briefing = _roles.MindGetBriefing(mindId);
// WD EDIT Get memories
foreach (var memory in mind.AllMemories)
{
memories[memory.Name] = memory.Value;
}
}
RaiseNetworkEvent(new CharacterInfoEvent(GetNetEntity(entity), jobTitle, objectives, briefing), args.SenderSession);
RaiseNetworkEvent(new CharacterInfoEvent(GetNetEntity(entity), jobTitle, objectives, briefing, memories), args.SenderSession);
}
}

View File

@@ -5,7 +5,11 @@ using Content.Server.Chat.Systems;
using Content.Server.Emp;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Stack;
using Content.Server.Storage.Components;
using Content.Server.Store.Components;
using Content.Server.UserInterface;
using Content.Server.White.Economy;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Actions;
@@ -15,9 +19,14 @@ using Content.Shared.DoAfter;
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.VendingMachines;
using Content.Shared.White.Economy;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
@@ -37,9 +46,16 @@ namespace Content.Server.VendingMachines
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ChatSystem _chat = default!;
// WD START
[Dependency] private readonly BankCardSystem _bankCard = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly StackSystem _stackSystem = default!;
// WD END
private ISawmill _sawmill = default!;
private double _priceMultiplier = 1.0; // WD
public override void Initialize()
{
base.Initialize();
@@ -60,6 +76,12 @@ namespace Content.Server.VendingMachines
SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
SubscribeLocalEvent<VendingMachineComponent, RestockDoAfterEvent>(OnDoAfter);
//WD EDIT
SubscribeLocalEvent<VendingMachineComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<VendingMachineComponent, VendingMachineWithdrawMessage>(OnWithdrawMessage);
//WD EDIT END
SubscribeLocalEvent<VendingMachineRestockComponent, PriceCalculationEvent>(OnPriceCalculation);
}
@@ -111,7 +133,8 @@ namespace Content.Server.VendingMachines
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); // WD EDIT
_userInterfaceSystem.TrySetUiState(uid, VendingMachineUiKey.Key, state);
}
@@ -190,6 +213,54 @@ namespace Content.Server.VendingMachines
args.Handled = true;
}
//WD EDIT
private void OnInteractUsing(EntityUid uid, VendingMachineComponent component, InteractUsingEvent args)
{
if (args.Handled)
return;
if (component.Broken || !this.IsPowered(uid, EntityManager))
return;
if (!TryComp<CurrencyComponent>(args.Used, out var currency) ||
!currency.Price.Keys.Contains(component.CurrencyType))
return;
var stack = Comp<StackComponent>(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)
{
return (int) _pricing.GetEstimatedPrice(proto);
}
private int GetPrice(VendingMachineInventoryEntry entry, VendingMachineComponent comp)
{
return (int) (entry.Price * GetPriceMultiplier(comp));
}
private double GetPriceMultiplier(VendingMachineComponent comp)
{
return comp.PriceMultiplier * _priceMultiplier;
}
private void OnWithdrawMessage(EntityUid uid, VendingMachineComponent component, VendingMachineWithdrawMessage args)
{
_stackSystem.Spawn(component.Credits,
PrototypeManager.Index<StackPrototype>(component.CreditStackPrototype), Transform(uid).Coordinates);
component.Credits = 0;
Audio.PlayPvs(component.SoundWithdrawCurrency, uid);
UpdateVendingMachineInterfaceState(uid, component);
}
//WD EDIT END
/// <summary>
/// Sets the <see cref="VendingMachineComponent.CanShoot"/> property of the vending machine.
/// </summary>
@@ -245,7 +316,7 @@ namespace Content.Server.VendingMachines
/// <param name="itemId">The prototype ID of the item</param>
/// <param name="throwItem">Whether the item should be thrown in a random direction after ejection</param>
/// <param name="vendComponent"></param>
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) // WD EDIT
{
if (!Resolve(uid, ref vendComponent))
return;
@@ -274,6 +345,44 @@ namespace Content.Server.VendingMachines
if (string.IsNullOrEmpty(entry.ID))
return;
// WD 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<BankCardComponent>(nextItem, out var bankCard) || !bankCard.BankAccountId.HasValue
|| !_bankCard.TryGetAccount(bankCard.BankAccountId.Value, out var account)
|| account.Balance < price)
continue;
_bankCard.TryChangeBalance(bankCard.BankAccountId.Value, -price);
success = true;
break;
}
}
if (!success)
{
Popup.PopupEntity(Loc.GetString("vending-machine-component-no-balance"), uid);
Deny(uid, vendComponent);
return;
}
}
// WD END
// Start Ejecting, and prevent users from ordering while anim playing
vendComponent.Ejecting = true;
@@ -297,7 +406,7 @@ namespace Content.Server.VendingMachines
{
if (IsAuthorized(uid, sender, component))
{
TryEjectVendorItem(uid, type, itemId, component.CanShoot, component);
TryEjectVendorItem(uid, type, itemId, component.CanShoot, component, sender);
}
}

View File

@@ -0,0 +1,124 @@
using System.Linq;
using Content.Server.Stack;
using Content.Server.Store.Components;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Stacks;
using Content.Shared.White.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.White.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<ATMComponent, EntInsertedIntoContainerMessage>(OnCardInserted);
SubscribeLocalEvent<ATMComponent, EntRemovedFromContainerMessage>(OnCardRemoved);
SubscribeLocalEvent<ATMComponent, ATMRequestWithdrawMessage>(OnWithdrawRequest);
SubscribeLocalEvent<ATMComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<ATMComponent, ComponentStartup>(OnComponentStartup);
}
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<CurrencyComponent>(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<StackComponent>(args.Used);
var bankCard = Comp<BankCardComponent>(component.CardSlot.Item.Value);
var amount = stack.Count;
_bankCardSystem.TryChangeBalance(bankCard.BankAccountId!.Value, amount);
Del(args.Used);
_audioSystem.PlayPvs(component.SoundInsertCurrency, uid);
UpdateUiState(uid, _bankCardSystem.GetBalance(bankCard.BankAccountId.Value), true, Loc.GetString("atm-ui-select-withdraw-amount"));
}
private void OnCardInserted(EntityUid uid, ATMComponent component, EntInsertedIntoContainerMessage args)
{
if (!TryComp<BankCardComponent>(args.Entity, out var bankCard) || !bankCard.BankAccountId.HasValue)
{
_container.EmptyContainer(args.Container);
return;
}
UpdateUiState(uid, _bankCardSystem.GetBalance(bankCard.BankAccountId.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<BankCardComponent>(component.CardSlot.Item, out var bankCard) || !bankCard.BankAccountId.HasValue)
{
if (component.CardSlot.ContainerSlot != null)
_container.EmptyContainer(component.CardSlot.ContainerSlot);
return;
}
if (!_bankCardSystem.TryGetAccount(bankCard.BankAccountId.Value, out var account) || account.AccountPin != args.Pin)
{
_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<StackPrototype>(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.TrySetUiState(uid, ATMUiKey.Key, state);
}
}

View File

@@ -0,0 +1,24 @@
using Content.Shared.Mind;
namespace Content.Server.White.Economy;
public sealed class BankAccount
{
public readonly int AccountId;
public readonly int AccountPin;
public int Balance;
public bool CommandBudgetAccount;
public Entity<MindComponent>? Mind;
public string Name = string.Empty;
public EntityUid? CartridgeUid;
public EntityUid? LoaderUid;
public BankAccount(int accountId, int balance)
{
AccountId = accountId;
Balance = balance;
AccountPin = Random.Shared.Next(1000, 10000);
}
}

View File

@@ -0,0 +1,221 @@
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.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!;
private const int SalaryDelay = 1200;
private SalaryPrototype _salaries = default!;
private readonly List<BankAccount> _accounts = new();
private float _salaryTimer;
public override void Initialize()
{
_salaries = _protoMan.Index<SalaryPrototype>("Salaries");
SubscribeLocalEvent<BankCardComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawned);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
_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.BankAccountId = acc.BankAccount.AccountId;
return;
}
if (component.BankAccountId.HasValue)
{
CreateAccount(component.BankAccountId.Value, component.StartingBalance);
return;
}
var account = CreateAccount(default, component.StartingBalance);
component.BankAccountId = account.AccountId;
}
private void OnRoundRestart(RoundRestartCleanupEvent ev)
{
_accounts.Clear();
}
private void OnPlayerSpawned(PlayerSpawnCompleteEvent ev)
{
if (_idCardSystem.TryFindIdCard(ev.Mob, out var id) && TryComp<MindContainerComponent>(ev.Mob, out var mind))
{
var cardEntity = id.Owner;
var bankCardComponent = EnsureComp<BankCardComponent>(cardEntity);
if (!bankCardComponent.BankAccountId.HasValue || !TryGetAccount(bankCardComponent.BankAccountId.Value, out var bankAccount))
return;
if (!TryComp(mind.Mind, out MindComponent? mindComponent))
return;
bankAccount.Balance = GetSalary(mind.Mind) * 3;
mindComponent.AddMemory(new Memory("PIN", bankAccount.AccountPin.ToString()));
bankAccount.Mind = (mind.Mind.Value, mindComponent);
bankAccount.Name = Name(ev.Mob);
if (!_inventorySystem.TryGetSlotEntity(ev.Mob, "id", out var pdaUid) ||
!TryComp(pdaUid, out CartridgeLoaderComponent? cartridgeLoader))
return;
BankCartridgeComponent? comp = null;
var programs = new List<EntityUid>(cartridgeLoader.BackgroundPrograms);
if (cartridgeLoader.ActiveProgram != null)
programs.Add(cartridgeLoader.ActiveProgram.Value);
var program = programs.Find(program => TryComp(program, out comp));
if (comp == null)
return;
bankAccount.CartridgeUid = program;
bankAccount.LoaderUid = pdaUid;
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<StationBankAccountComponent>().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 is {CartridgeUid: not null, LoaderUid: not null})
_bankCartridge.UpdateUiState(account.CartridgeUid.Value, account.LoaderUid.Value);
return true;
}
}

View File

@@ -0,0 +1,8 @@
namespace Content.Server.White.Economy;
[RegisterComponent]
public sealed partial class BankCartridgeComponent : Component
{
[ViewVariables]
public int? AccountId;
}

View File

@@ -0,0 +1,46 @@
using Content.Server.CartridgeLoader;
using Content.Shared.CartridgeLoader;
using Content.Shared.White.Economy;
namespace Content.Server.White.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<BankCartridgeComponent, CartridgeMessageEvent>(OnUiMessage);
SubscribeLocalEvent<BankCartridgeComponent, CartridgeUiReadyEvent>(OnUiReady);
}
private void OnUiReady(EntityUid uid, BankCartridgeComponent component, CartridgeUiReadyEvent args)
{
UpdateUiState(uid, args.Loader, component);
}
private void OnUiMessage(EntityUid uid, BankCartridgeComponent component, CartridgeMessageEvent args)
{
UpdateUiState(uid, GetEntity(args.LoaderUid), component);
}
public void UpdateUiState(EntityUid cartridgeUid, EntityUid loaderUid, BankCartridgeComponent? component = null)
{
if (!Resolve(cartridgeUid, ref component))
return;
var state = new BankCartridgeUiState
{
AccountLinked = component.AccountId != null
};
if (component.AccountId != null && _bankCardSystem.TryGetAccount(component.AccountId.Value, out var account))
{
state.Balance = account.Balance;
state.OwnerName = account.Name;
}
_cartridgeLoaderSystem?.UpdateCartridgeUiState(loaderUid, state);
}
}

View File

@@ -0,0 +1,6 @@
namespace Content.Server.White.Economy;
[RegisterComponent]
public sealed partial class CommandBudgetPinPaperComponent : Component
{
}

View File

@@ -0,0 +1,27 @@
using Content.Server.Cargo.Components;
using Content.Server.Paper;
using Content.Server.Station.Systems;
namespace Content.Server.White.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<CommandBudgetPinPaperComponent, MapInitEvent>(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)));
}
}

View File

@@ -0,0 +1,88 @@
using Content.Shared.Access.Components;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.White.Economy;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
namespace Content.Server.White.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<EftposComponent, EftposLockMessage>(OnLock);
SubscribeLocalEvent<EftposComponent, InteractUsingEvent>(OnInteractUsing);
}
private void OnInteractUsing(EntityUid uid, EftposComponent component, InteractUsingEvent args)
{
if (component.BankAccountId == null || !TryComp(args.Used, out BankCardComponent? bankCard) ||
bankCard.BankAccountId == component.BankAccountId || component.Amount <= 0)
return;
if (_bankCardSystem.TryChangeBalance(bankCard.BankAccountId!.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.Session.AttachedEntity, out HandsComponent? hands) ||
!TryComp(hands.ActiveHandEntity, out BankCardComponent? bankCard))
return;
if (component.BankAccountId == null)
{
component.BankAccountId = bankCard.BankAccountId;
component.Amount = args.Amount;
}
else if (component.BankAccountId == bankCard.BankAccountId)
{
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.TrySetUiState(uid, EftposKey.Key, state);
}
}

View File

@@ -21,12 +21,14 @@ public sealed class CharacterInfoEvent : EntityEventArgs
public readonly string JobTitle;
public readonly Dictionary<string, List<ObjectiveInfo>> Objectives;
public readonly string? Briefing;
public readonly Dictionary<string, string> Memory;
public CharacterInfoEvent(NetEntity netEntity, string jobTitle, Dictionary<string, List<ObjectiveInfo>> objectives, string? briefing)
public CharacterInfoEvent(NetEntity netEntity, string jobTitle, Dictionary<string, List<ObjectiveInfo>> objectives, string? briefing, Dictionary<string,string> memory)
{
NetEntity = netEntity;
JobTitle = jobTitle;
Objectives = objectives;
Briefing = briefing;
Memory = memory;
}
}

View File

@@ -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;
}
}

View File

@@ -29,6 +29,8 @@ namespace Content.Shared.Mind
[DataField, AutoNetworkedField]
public List<EntityUid> Objectives = new();
internal readonly List<Memory> _memories = new();
/// <summary>
/// The session ID of the player owning this mind.
/// </summary>
@@ -73,6 +75,21 @@ namespace Content.Shared.Mind
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
public string? BorgName { get; set; }
[ViewVariables]
public IEnumerable<Memory> AllMemories => _memories;
//WD EDIT
public void AddMemory(Memory memory)
{
if (_memories.Contains(memory))
{
return;
}
_memories.Add(memory);
}
//WD EDIT END
/// <summary>
/// The time of death for this Mind.
/// Can be null - will be null if the Mind is not considered "dead".

View File

@@ -105,7 +105,7 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem
foreach (var (id, amount) in entries)
{
if (PrototypeManager.HasIndex<EntityPrototype>(id))
if (PrototypeManager.TryIndex<EntityPrototype>(id, out var proto)) // WD EDIT
{
if (inventory.TryGetValue(id, out var entry))
// Prevent a machine's stock from going over three times
@@ -115,9 +115,14 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem
// all the items just to restock one empty slot without
// losing the rest of the restock.
entry.Amount = Math.Min(entry.Amount + amount, 3 * amount);
else
inventory.Add(id, new VendingMachineInventoryEntry(type, id, amount));
else // WD EDIT START
{
var price = GetEntryPrice(proto);
inventory.Add(id, new VendingMachineInventoryEntry(type, id, amount, price));
} // WD EDIT END
}
}
}
protected virtual int GetEntryPrice(EntityPrototype proto) { return 0; } // WD
}

View File

@@ -1,4 +1,6 @@
using Content.Shared.Actions;
using Content.Shared.Stacks;
using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
@@ -179,6 +181,27 @@ namespace Content.Shared.VendingMachines
[DataField("loopDeny")]
public bool LoopDenyAnimation = true;
#endregion
//WD EDIT
[DataField("priceMultiplier")]
public double PriceMultiplier;
[ValidatePrototypeId<StackPrototype>]
public string CreditStackPrototype = "Credit";
[DataField("currencyType")]
public string CurrencyType = "SpaceCash";
[DataField("soundInsertCurrency")]
public SoundSpecifier SoundInsertCurrency = new SoundPathSpecifier("/Audio/White/Machines/polaroid2.ogg");
[DataField("soundWithdrawCurrency")]
public SoundSpecifier SoundWithdrawCurrency = new SoundPathSpecifier("/Audio/White/Machines/polaroid1.ogg");
[ViewVariables]
public int Credits;
//WD EDIT END
}
[Serializable, NetSerializable]
@@ -190,11 +213,14 @@ namespace Content.Shared.VendingMachines
public string ID;
[ViewVariables(VVAccess.ReadWrite)]
public uint Amount;
public VendingMachineInventoryEntry(InventoryType type, string id, uint amount)
[ViewVariables(VVAccess.ReadWrite)]
public int Price; // WD
public VendingMachineInventoryEntry(InventoryType type, string id, uint amount, int price)
{
Type = type;
ID = id;
Amount = amount;
Price = price; // WD
}
}

View File

@@ -6,19 +6,31 @@ namespace Content.Shared.VendingMachines
public sealed class VendingMachineInterfaceState : BoundUserInterfaceState
{
public List<VendingMachineInventoryEntry> Inventory;
// WD EDIT START
public double PriceMultiplier;
public int Credits;
public VendingMachineInterfaceState(List<VendingMachineInventoryEntry> inventory)
public VendingMachineInterfaceState(List<VendingMachineInventoryEntry> inventory, double priceMultiplier,
int credits)
{
Inventory = inventory;
PriceMultiplier = priceMultiplier;
Credits = credits;
}
}
[Serializable, NetSerializable]
public sealed class VendingMachineWithdrawMessage : BoundUserInterfaceMessage
{
}
// WD EDIT END
[Serializable, NetSerializable]
public sealed class VendingMachineEjectMessage : BoundUserInterfaceMessage
{
public readonly InventoryType Type;
public readonly string ID;
public VendingMachineEjectMessage(InventoryType type, string id)
public VendingMachineEjectMessage(InventoryType type, string id) // WD EDIT
{
Type = type;
ID = id;

View File

@@ -0,0 +1,40 @@
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Stacks;
using Robust.Shared.Audio;
using Robust.Shared.Serialization;
namespace Content.Shared.White.Economy;
[RegisterComponent]
public sealed partial class ATMComponent : Component
{
[DataField("idCardSlot")]
public ItemSlot CardSlot = default!;
[DataField("currencyType")]
public string CurrencyType = "SpaceCash";
public string SlotId = "card-slot";
[ValidatePrototypeId<StackPrototype>]
public string CreditStackPrototype = "Credit";
[DataField("soundInsertCurrency")]
public SoundSpecifier SoundInsertCurrency = new SoundPathSpecifier("/Audio/White/Machines/polaroid2.ogg");
[DataField("soundWithdrawCurrency")]
public SoundSpecifier SoundWithdrawCurrency = new SoundPathSpecifier("/Audio/White/Machines/polaroid1.ogg");
[DataField("soundApply")]
public SoundSpecifier SoundApply = new SoundPathSpecifier("/Audio/White/Machines/chime.ogg");
[DataField("soundDeny")]
public SoundSpecifier SoundDeny = new SoundPathSpecifier("/Audio/White/Machines/buzz-sigh.ogg");
}
[Serializable, NetSerializable]
public enum ATMUiKey
{
Key
}

View File

@@ -0,0 +1,24 @@
using Robust.Shared.Serialization;
namespace Content.Shared.White.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;
}

View File

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

View File

@@ -0,0 +1,11 @@
using Robust.Shared.Serialization;
namespace Content.Shared.White.Economy;
[Serializable, NetSerializable]
public sealed class BankCartridgeUiState : BoundUserInterfaceState
{
public int Balance;
public string OwnerName = string.Empty;
public bool AccountLinked;
}

View File

@@ -0,0 +1,26 @@
using Robust.Shared.Audio;
using Robust.Shared.Serialization;
namespace Content.Shared.White.Economy;
[RegisterComponent]
public sealed partial class EftposComponent : Component
{
[ViewVariables]
public int? BankAccountId;
[ViewVariables]
public int Amount;
[DataField("soundApply")]
public SoundSpecifier SoundApply = new SoundPathSpecifier("/Audio/White/Machines/chime.ogg");
[DataField("soundDeny")]
public SoundSpecifier SoundDeny = new SoundPathSpecifier("/Audio/White/Machines/buzz-sigh.ogg");
}
[Serializable, NetSerializable]
public enum EftposKey
{
Key
}

View File

@@ -0,0 +1,22 @@
using Robust.Shared.Serialization;
namespace Content.Shared.White.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;
}
}

View File

@@ -0,0 +1,13 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.White.Economy;
[Prototype("salary")]
public sealed class SalaryPrototype : IPrototype
{
[IdDataField]
public string ID { get; } = default!;
[DataField("salaries")]
public Dictionary<string, int> Salaries = new();
}

View File

@@ -0,0 +1,28 @@
using Content.Shared.Containers.ItemSlots;
using Robust.Shared.Containers;
namespace Content.Shared.White.Economy;
public abstract class SharedATMSystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ATMComponent, ComponentInit>(OnATMInit);
SubscribeLocalEvent<ATMComponent, ComponentRemove>(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);
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -9,3 +9,8 @@ store-currency-display-debugdollar = {$amount ->
}
store-currency-display-telecrystal = TC
store-currency-display-stolen-essence = Stolen Essence
store-currency-display-space-cash = {$amount ->
[one] One Dollar
*[other] Dollars
}

View File

@@ -9,3 +9,10 @@ store-currency-display-debugdollar =
}
store-currency-display-telecrystal = ТК
store-currency-display-stolen-essence = Украденная эссенция
store-currency-display-space-cash =
{ $amount ->
[one] { $amount } доллар
[few] { $amount } доллара
*[other] { $amount } долларов
}

View File

@@ -0,0 +1 @@
salary-pay-announcement = Внимание! Экипажу станции была выплачена заработная плата.

View File

@@ -0,0 +1,12 @@
atm-trying-insert-cash-error = Купюроприемник закрыт.
atm-wrong-pin = Неверный PIN-код.
atm-not-enough-cash = Недостаточно средств.
atm-ui-select-withdraw-amount = Выберите сумму вывода.
atm-ui-insert-card = Вставьте карту.
atm-ui-balance = Баланс: { $balance } ¢
ent-ATM = Банкомат
.desc = Для всех ваших денежных нужд!
.suffix = { "" }

View File

@@ -0,0 +1,6 @@
bank-program-ui-no-account = [color=red]Аккаунт не привязан.[/color]
bank-program-name = Банк
ent-BankCartridge = Картридж банка
.desc = Программа для управления банковским аккаунтом.
.suffix = { "" }

View File

@@ -0,0 +1,32 @@
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-код станционного бюджета.
.suffix = { "" }
command-budget-pin-message = PIN-код станционного бюджета: { $pin }

View File

@@ -0,0 +1,16 @@
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 = Ошибка транзакции
command-budget = Командный бюджет
ent-Eftpos = Терминал безналичной оплаты
.desc = Проведите картой для совершения покупок без использования наличных.
.suffix = { "" }

View File

@@ -0,0 +1,2 @@
character-info-memories-label = Память
character-info-memories-placeholder-text = Воспоминания

View File

@@ -0,0 +1 @@
vending-ui-credits-amount = Кредитов в автомате: { $credits }

View File

@@ -34,7 +34,6 @@
- id: WeaponEgun
- id: CommsComputerCircuitboard
- id: ClothingHeadsetAltCommand
- id: SpaceCash1000
- id: PlushieNuke
prob: 0.1
- id: CigarGoldCase
@@ -45,6 +44,8 @@
- id: WeaponAntiqueLaser
- id: JetpackCaptainFilled
- id: MedalCase
- id: CommandBudgetCard
- id: CommandBudgetPinPaper
- type: entity
id: LockerCaptainFilled
@@ -60,7 +61,6 @@
- id: WeaponDisabler
- id: CommsComputerCircuitboard
- id: ClothingHeadsetAltCommand
- id: SpaceCash1000
- id: PlushieNuke
prob: 0.1
- id: CigarGoldCase
@@ -71,6 +71,8 @@
- id: WeaponAntiqueLaser
- id: JetpackCaptainFilled
- id: MedalCase
- id: CommandBudgetCard
- id: CommandBudgetPinPaper
- type: entity
id: LockerHeadOfPersonnelFilled
@@ -168,6 +170,7 @@
- id: RubberStampCMO
- id: MedicalTechFabCircuitboard
- id: BoxEncryptionKeyMedical
- id: Eftpos
- type: entity
id: LockerChiefMedicalOfficerFilled
@@ -188,6 +191,7 @@
- id: RubberStampCMO
- id: MedicalTechFabCircuitboard
- id: BoxEncryptionKeyMedical
- id: Eftpos
- type: entity
id: LockerResearchDirectorFilledHardsuit

View File

@@ -102,6 +102,7 @@
prob: 0.05
orGroup: Surgshrubs
- id: ClothingMaskSterile
- id: Eftpos
- type: entity
id: LockerChemistryFilled
@@ -123,6 +124,7 @@
- id: ClothingMaskSterile
- id: HandLabeler
prob: 0.5
- id: Eftpos
- type: entity
id: LockerParamedicFilled
@@ -144,3 +146,4 @@
- id: ClothingMaskSterile
- id: MedkitFilled
prob: 0.3
- id: Eftpos

View File

@@ -19,6 +19,7 @@
amount: 2
- id: RagItem
amount: 2
- id: Eftpos
#- type: entity
# id: LockerFormalFilled
@@ -50,6 +51,7 @@
- id: DrinkMilkCarton
amount: 2
- id: DrinkSoyMilkCarton
- id: Eftpos
- type: entity
id: ClosetJanitorFilled
@@ -106,6 +108,7 @@
- id: ClothingUniformOveralls
- id: ClothingHeadHatTrucker
prob: 0.1
- id: Eftpos
- type: entity
id: LockerBotanistLoot

View File

@@ -34,3 +34,4 @@
DrinkWhiskeyBottleFull: 5
DrinkWineBottleFull: 5
DrinkChampagneBottleFull: 2 #because the premium drink
Eftpos: 4

View File

@@ -19,3 +19,4 @@
FoodButter: 4
FoodCheese: 1
FoodMeat: 6
Eftpos: 4

View File

@@ -7,3 +7,4 @@
Bloodpack: 5
EpinephrineChemistryBottle: 3
Syringe: 5
Eftpos: 2 #WD edit

View File

@@ -16,6 +16,7 @@
EZNutrientChemistryBottle: 3
Bucket: 3
DiseaseSwab: 20
Eftpos: 4 #WD edit
#TO DO:
#plant analyzer
emaggedInventory:

View File

@@ -52,7 +52,7 @@
- key: enum.StorageUiKey.Key
type: StorageBoundUserInterface
- type: StaticPrice
price: 80
price: 20
# WD edit sounds start
- type: EmitSoundOnPickup
sound:

View File

@@ -13,6 +13,7 @@
- BypassInteractionRangeChecks
- BypassDropChecks
- SMImmune
- IgnoreBalanceChecks
- type: Input
context: "aghost"
- type: Ghost

View File

@@ -35,6 +35,8 @@
interfaces:
- key: enum.TransferAmountUiKey.Key
type: TransferAmountBoundUserInterface
- type: StaticPrice
price: 30
- type: entity
parent: DrinkBase

View File

@@ -62,6 +62,8 @@
- type: Tag
tags:
- DrinkCan
- type: StaticPrice
price: 20
- type: entity
parent: DrinkCanBaseFull

View File

@@ -37,6 +37,8 @@
damage:
types:
Blunt: 0
- type: StaticPrice
price: 20
- type: entity
parent: DrinkBaseCup

View File

@@ -22,6 +22,8 @@
- type: Item
sprite: Objects/Consumable/Food/Baked/donut.rsi
size: Tiny
- type: StaticPrice
price: 10
# Tastes like donut.
# The sprinkles are now an overlay, so you can put them on any donut! If we really

View File

@@ -17,7 +17,7 @@
solution: food
- type: SpaceGarbage
- type: StaticPrice
price: 0
price: 5
# Packets

View File

@@ -23,6 +23,8 @@
sprite: Objects/Consumable/Food/snacks.rsi
heldPrefix: packet
size: Tiny
- type: StaticPrice
price: 10
# Snacks
# "Snacks" means food in a packet. Down the line this stuff can have multiple
@@ -112,6 +114,8 @@
- id: FoodSnackChocolateBar
sound:
path: /Audio/Effects/unwrap.ogg
- type: StaticPrice
price: 20
- type: entity
name: chocolate bar

View File

@@ -64,7 +64,9 @@
- cig4
- cig5
- cig6
- type: Appearance
- type: Appearance
- type: StaticPrice
price: 10
- type: entity
id: CigPackMixedBase

View File

@@ -61,6 +61,8 @@
- cigar7
- cigar8
- type: Appearance
- type: StaticPrice
price: 40
- type: entity
id: CigarGoldCase
@@ -72,3 +74,5 @@
contents:
- id: CigarGold
amount: 8
- type: StaticPrice
price: 80

View File

@@ -55,6 +55,8 @@
equippedPrefix: unlit
- type: Item
size: Tiny
- type: StaticPrice
price: 10
- type: entity
id: CigarGoldSpent

View File

@@ -62,6 +62,8 @@
solutions:
smokable:
maxVol: 30
- type: StaticPrice
price: 20
- type: entity
parent: BaseItem
@@ -80,3 +82,5 @@
solution: smokable
- type: ExaminableSolution
solution: smokable
- type: StaticPrice
price: 20

View File

@@ -75,6 +75,7 @@
- CrewManifestCartridge
- NotekeeperCartridge
- NewsReadCartridge
- BankCartridge
cartridgeSlot:
priority: -1
name: Cartridge

View File

@@ -10,6 +10,8 @@
Credit: 1
- type: StaticPrice
price: 0
- type: StackPrice
price: 1
- type: Stack
stackType: Credit
count: 1
@@ -39,6 +41,9 @@
mask:
- ItemMask
- type: Appearance
- type: Currency
price:
SpaceCash: 1
- type: material
id: Credit

View File

@@ -86,6 +86,9 @@
netsync: false
radius: 1.1 #smallest possible
color: orange
- type: StaticPrice
price: 10
- type: entity
name: cheap lighter
@@ -115,6 +118,8 @@
- ReagentId: WeldingFuel
Quantity: 4
maxVol: 4 #uses less fuel than a welder, so this isnt as bad as it looks
- type: StaticPrice
price: 5
- type: entity
name: flippo lighter

View File

@@ -109,6 +109,7 @@
- type: VendingMachine
pack: CondimentInventory
offState: off
priceMultiplier: 1.0
- type: Sprite
sprite: Structures/Machines/VendingMachines/condiments.rsi
drawdepth: SmallObjects
@@ -265,6 +266,7 @@
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: CigaretteMachineAds
- type: Speech
@@ -291,6 +293,7 @@
brokenState: broken
normalState: normal-unshaded
denyState: deny-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: ClothesMateAds
- type: Speech
@@ -324,6 +327,7 @@
brokenState: broken
normalState: normal-unshaded
denyState: deny-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: ClothesMateAds
- type: Speech
@@ -363,6 +367,7 @@
screenState: screen
ejectDelay: 5
soundVend: /Audio/Machines/machine_vend_hot_drink.ogg
priceMultiplier: 1.0
- type: Advertise
pack: HotDrinksMachineAds
- type: Speech
@@ -400,6 +405,7 @@
ejectState: eject-unshaded
denyState: deny-unshaded
ejectDelay: 1.9
priceMultiplier: 1.0
- type: Advertise
pack: RobustSoftdrinksAds
- type: Speech
@@ -467,6 +473,7 @@
components:
- type: VendingMachine
pack: SpaceUpInventory
priceMultiplier: 1.0
- type: Sprite
sprite: Structures/Machines/VendingMachines/spaceup.rsi
layers:
@@ -489,6 +496,7 @@
components:
- type: VendingMachine
pack: SodaInventory
priceMultiplier: 1.0
- type: Sprite
sprite: Structures/Machines/VendingMachines/soda.rsi
layers:
@@ -512,6 +520,7 @@
components:
- type: VendingMachine
pack: StarkistInventory
priceMultiplier: 1.0
- type: Sprite
sprite: Structures/Machines/VendingMachines/starkist.rsi
layers:
@@ -543,6 +552,7 @@
ejectState: eject-unshaded
denyState: deny-unshaded
ejectDelay: 1.9
priceMultiplier: 1.0
- type: Advertise
pack: RobustSoftdrinksAds
- type: Speech
@@ -577,6 +587,7 @@
ejectState: eject-unshaded
denyState: deny-unshaded
ejectDelay: 1.9
priceMultiplier: 1.0
- type: Advertise
pack: RobustSoftdrinksAds
- type: Speech
@@ -701,6 +712,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: DiscountDansAds
- type: Speech
@@ -906,6 +918,7 @@
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: GetmoreChocolateCorpAds
- type: Speech
@@ -939,6 +952,7 @@
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
priceMultiplier: 1.0
- type: Sprite
sprite: Structures/Machines/VendingMachines/sustenance.rsi
layers:
@@ -1049,6 +1063,7 @@
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: BodaAds
- type: Speech
@@ -1081,6 +1096,7 @@
ejectState: eject-unshaded
denyState: deny-unshaded
screenState: screen
priceMultiplier: 1.0
- type: Advertise
pack: AutoDrobeAds
- type: Speech
@@ -1246,6 +1262,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: ChangAds
- type: Speech
@@ -1309,6 +1326,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: DonutAds
- type: Speech
@@ -1384,6 +1402,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: HyDrobeAds
- type: Sprite
@@ -1410,6 +1429,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: LawDrobeAds
- type: Sprite
@@ -1436,6 +1456,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: SecDrobeAds
- type: Sprite
@@ -1462,6 +1483,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: BarDrobeAds
- type: Sprite
@@ -1488,6 +1510,7 @@
brokenState: broken
normalState: normal-unshaded
denyState: deny-unshaded
priceMultiplier: 1.0
- type: Sprite
sprite: Structures/Machines/VendingMachines/chapdrobe.rsi
layers:
@@ -1516,6 +1539,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: CargoDrobeAds
- type: Sprite
@@ -1542,6 +1566,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: MediDrobeAds
- type: Sprite
@@ -1568,6 +1593,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: ChemDrobeAds
- type: Sprite
@@ -1620,6 +1646,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: AtmosDrobeAds
- type: Sprite
@@ -1646,6 +1673,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: EngiDrobeAds
- type: Sprite
@@ -1672,6 +1700,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: ChefDrobeAds
- type: Sprite
@@ -1698,6 +1727,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: DetDrobeAds
- type: Sprite
@@ -1724,6 +1754,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: JaniDrobeAds
- type: Sprite
@@ -1750,6 +1781,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: SciDrobeAds
- type: Sprite
@@ -1802,6 +1834,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: RoboDrobeAds
- type: Sprite
@@ -1828,6 +1861,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: GeneDrobeAds
- type: Sprite
@@ -1854,6 +1888,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Advertise
pack: ViroDrobeAds
- type: Sprite
@@ -1880,6 +1915,7 @@
offState: off
brokenState: broken
normalState: normal-unshaded
priceMultiplier: 1.0
- type: Sprite
sprite: Structures/Machines/VendingMachines/centdrobe.rsi
layers:
@@ -1913,6 +1949,7 @@
denyState: deny-unshaded
ejectDelay: 1.9
soundVend: /Audio/Items/bikehorn.ogg
priceMultiplier: 1.0
- type: Sprite
sprite: Structures/Machines/VendingMachines/happyhonk.rsi
layers:

View File

@@ -100,6 +100,11 @@
kind: source
path: "/Textures/Shaders/cataracts.swsl"
- type: shader
id: Gradient
kind: source
path: "/Textures/Shaders/White/gradient.swsl"
- type: shader
id: SaturationScale
kind: source

View File

@@ -19,3 +19,9 @@
- type: currency
id: DebugDollar
displayName: store-currency-display-debugdollar
- type: currency
id: SpaceCash
cash :
1: SpaceCash
displayName: store-currency-display-space-cash

View File

@@ -0,0 +1,95 @@
- type: entity
parent: BaseItem
id: BaseDepartmentBudgetCard
noSpawn: true
components:
- type: BankCard
startingBalance: 5000
- type: Sprite
sprite: White/Misc/cards.rsi
scale: 0.8, 0.8
- type: entity
parent: BaseDepartmentBudgetCard
id: CommandBudgetCard
name: Command budget card
components:
- type: Sprite
state: budgetcard
- type: BankCard
commandBudgetCard: true
- type: entity
parent: Paper
id: CommandBudgetPinPaper
name: command budget pin
components:
- type: CommandBudgetPinPaper
- type: Paper
- type: entity
parent: BaseDepartmentBudgetCard
id: CargoBudgetCard
noSpawn: true
name: Cargo budget card
components:
- type: Sprite
state: car_budget
- type: BankCard
accountId: 1315
- type: entity
parent: BaseDepartmentBudgetCard
id: CivilianBudgetCard
name: Civilian budget card
noSpawn: true
components:
- type: Sprite
state: srv_budget
- type: BankCard
accountId: 1316
- type: entity
parent: BaseDepartmentBudgetCard
id: EngineeringBudgetCard
name: Engineering budget card
noSpawn: true
components:
- type: Sprite
state: eng_budget
- type: BankCard
accountId: 1318
- type: entity
parent: BaseDepartmentBudgetCard
id: MedicalBudgetCard
name: Medical budget card
noSpawn: true
components:
- type: Sprite
state: med_budget
- type: BankCard
accountId: 1319
- type: entity
parent: BaseDepartmentBudgetCard
id: ScienceBudgetCard
name: Science budget card
noSpawn: true
components:
- type: Sprite
state: sci_budget
- type: BankCard
accountId: 1321
- type: entity
parent: BaseDepartmentBudgetCard
id: SecurityBudgetCard
name: Security budget card
noSpawn: true
components:
- type: Sprite
state: sec_budget
- type: BankCard
accountId: 1320

View File

@@ -0,0 +1,27 @@
- type: entity
parent: BaseItem
id: Eftpos
name: EFTPOS
description: Swipe your ID card to make purchases electronically.
components:
- type: Sprite
sprite: White/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:
- key: enum.EftposKey.Key
type: EftposBui
- type: Clothing
quickEquip: false
slots:
- Belt

View File

@@ -0,0 +1,42 @@
- type: salary
id: Salaries
salaries:
Captain: 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
Scientist: 300
ResearchAssistant: 200

View File

@@ -0,0 +1,21 @@
- type: entity
parent: BaseItem
id: BankCartridge
name: bank cartridge
description: A program for managing bank account
components:
- type: Sprite
sprite: Objects/Devices/cartridge.rsi
state: cart-y
netsync: false
- type: Icon
sprite: Objects/Devices/cartridge.rsi
state: cart-y
- type: UIFragment
ui: !type:BankUi
- type: Cartridge
programName: bank-program-name
icon:
sprite: White/Structures/Machines/atm.rsi
state: off
- type: BankCartridge

View File

@@ -0,0 +1,75 @@
- 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: White/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: Destructible
# thresholds:
# - trigger:
# !type:DamageTrigger
# damage: 100
# behaviors:
# - !type:DoActsBehavior
# acts: ["Breakage"]
# - !type:EjectVendorItems
- type: ActivatableUI
key: enum.ATMUiKey.Key
singleUser: true
- type: ActivatableUIRequiresPower
- type: UserInterface
interfaces:
- key: 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

View File

@@ -9,3 +9,6 @@
- type: Tag
id: SMImmune
- type: Tag
id: IgnoreBalanceChecks

View File

@@ -1199,4 +1199,6 @@
- type: Tag
id: ZombifyableByMelee
- type: Tag
id: SpaceCash
# WHITE END

View File

@@ -0,0 +1,20 @@
uniform sampler2D SCREEN_TEXTURE;
uniform highp vec4 first_color = vec4(1.0);
uniform highp vec4 second_color = vec4(0.0, 0.0, 0.0, 1.0);
uniform highp float position = 0.0;
uniform highp float size = 1.0;
uniform highp float angle = 90.0;
void fragment() {
highp float pivot = position + 0.5;
highp vec2 uv = UV - pivot;
highp float rotated = uv.x * cos(radians(angle)) - uv.y * sin(radians(angle));
highp float pos = smoothstep((1.0 - size) + position, size + 0.0001 + position, rotated + pivot);
COLOR = mix(first_color, second_color, pos);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

View File

@@ -0,0 +1,35 @@
{
"license": "CC-BY-SA-3.0",
"copyright": "da poxuy",
"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"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Some files were not shown because too many files have changed in this diff Show More