diff --git a/Content.Client/DeltaV/CartridgeLoader/Cartridges/PriceHistoryTable.xaml b/Content.Client/DeltaV/CartridgeLoader/Cartridges/PriceHistoryTable.xaml
new file mode 100644
index 000000000000..058bde07e9c6
--- /dev/null
+++ b/Content.Client/DeltaV/CartridgeLoader/Cartridges/PriceHistoryTable.xaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/DeltaV/CartridgeLoader/Cartridges/PriceHistoryTable.cs b/Content.Client/DeltaV/CartridgeLoader/Cartridges/PriceHistoryTable.xaml.cs
similarity index 56%
rename from Content.Client/DeltaV/CartridgeLoader/Cartridges/PriceHistoryTable.cs
rename to Content.Client/DeltaV/CartridgeLoader/Cartridges/PriceHistoryTable.xaml.cs
index 465958ab9821..f5798f44c42b 100644
--- a/Content.Client/DeltaV/CartridgeLoader/Cartridges/PriceHistoryTable.cs
+++ b/Content.Client/DeltaV/CartridgeLoader/Cartridges/PriceHistoryTable.xaml.cs
@@ -1,46 +1,22 @@
using System.Linq;
+using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
namespace Content.Client.DeltaV.CartridgeLoader.Cartridges;
-public sealed class PriceHistoryTable : BoxContainer
+[GenerateTypedNameReferences]
+public sealed partial class PriceHistoryTable : BoxContainer
{
- private readonly GridContainer _grid;
-
public PriceHistoryTable()
{
- Orientation = LayoutOrientation.Vertical;
- HorizontalExpand = true;
- Margin = new Thickness(0, 5, 0, 0);
+ RobustXamlLoader.Load(this);
- // Create header
- var header = new BoxContainer
- {
- Orientation = LayoutOrientation.Horizontal,
- HorizontalExpand = true,
- };
-
- header.AddChild(new Label
- {
- Text = "Price History",
- HorizontalExpand = true,
- StyleClasses = { "LabelSubText" }
- });
-
- AddChild(header);
-
- // Create a panel container with styled background
- var panel = new PanelContainer
- {
- HorizontalExpand = true,
- Margin = new Thickness(0, 2, 0, 0)
- };
-
- // Create and apply the style
+ // Create the stylebox here so we can use the colors from StockTradingUi
var styleBox = new StyleBoxFlat
{
- BackgroundColor = StockTradingUiFragment.BackgroundColor,
+ BackgroundColor = StockTradingUiFragment.PriceBackgroundColor,
ContentMarginLeftOverride = 6,
ContentMarginRightOverride = 6,
ContentMarginTopOverride = 4,
@@ -49,30 +25,12 @@ public PriceHistoryTable()
BorderThickness = new Thickness(1),
};
- panel.PanelOverride = styleBox;
-
- // Create a centering container
- var centerContainer = new BoxContainer
- {
- Orientation = LayoutOrientation.Horizontal,
- HorizontalExpand = true,
- HorizontalAlignment = HAlignment.Center,
- };
-
- // Create grid for price history
- _grid = new GridContainer
- {
- Columns = 5, // Display 5 entries per row
- };
-
- centerContainer.AddChild(_grid);
- panel.AddChild(centerContainer);
- AddChild(panel);
+ HistoryPanel.PanelOverride = styleBox;
}
public void Update(List priceHistory)
{
- _grid.RemoveAllChildren();
+ PriceGrid.RemoveAllChildren();
// Take last 5 prices
var lastFivePrices = priceHistory.TakeLast(5).ToList();
@@ -111,7 +69,7 @@ public void Update(List priceHistory)
entryContainer.AddChild(priceLabel);
entryContainer.AddChild(changeLabel);
- _grid.AddChild(entryContainer);
+ PriceGrid.AddChild(entryContainer);
}
}
}
diff --git a/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUi.cs b/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUi.cs
index 07d3f24d920e..45704ee23499 100644
--- a/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUi.cs
+++ b/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUi.cs
@@ -36,7 +36,7 @@ public override void UpdateState(BoundUserInterfaceState state)
}
}
- private static void SendStockTradingUiMessage(StockTradingUiAction action, string company, float amount, BoundUserInterface userInterface)
+ private static void SendStockTradingUiMessage(StockTradingUiAction action, int company, float amount, BoundUserInterface userInterface)
{
var newsMessage = new StockTradingUiMessageEvent(action, company, amount);
var message = new CartridgeUiMessage(newsMessage);
diff --git a/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUiFragment.xaml b/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUiFragment.xaml
index 08491be82805..00b45584cc46 100644
--- a/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUiFragment.xaml
+++ b/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUiFragment.xaml
@@ -11,12 +11,12 @@
-
@@ -27,7 +27,7 @@
diff --git a/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUiFragment.xaml.cs b/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUiFragment.xaml.cs
index d0b85d58a3e6..b44e8f44c70f 100644
--- a/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUiFragment.xaml.cs
+++ b/Content.Client/DeltaV/CartridgeLoader/Cartridges/StockTradingUiFragment.xaml.cs
@@ -11,17 +11,18 @@ namespace Content.Client.DeltaV.CartridgeLoader.Cartridges;
[GenerateTypedNameReferences]
public sealed partial class StockTradingUiFragment : BoxContainer
{
- private readonly Dictionary _companyEntries = new();
+ private readonly Dictionary _companyEntries = new();
// Event handlers for the parent UI
- public event Action? OnBuyButtonPressed;
- public event Action? OnSellButtonPressed;
+ public event Action? OnBuyButtonPressed;
+ public event Action? OnSellButtonPressed;
// Define colors
public static readonly Color PositiveColor = Color.FromHex("#00ff00"); // Green
public static readonly Color NegativeColor = Color.FromHex("#ff0000"); // Red
public static readonly Color NeutralColor = Color.FromHex("#ffffff"); // White
public static readonly Color BackgroundColor = Color.FromHex("#25252a"); // Dark grey
+ public static readonly Color PriceBackgroundColor = Color.FromHex("#1a1a1a"); // Darker grey
public static readonly Color BorderColor = Color.FromHex("#404040"); // Light grey
public StockTradingUiFragment()
@@ -32,26 +33,25 @@ public StockTradingUiFragment()
public void UpdateState(StockTradingUiState state)
{
NoEntries.Visible = state.Entries.Count == 0;
- Balance.Text = $"Balance: {state.Balance:F2} credits";
+ Balance.Text = Loc.GetString("stock-trading-balance", ("balance", state.Balance));
- // Remove old entries
- foreach (var key in _companyEntries.Keys.ToList().Where(key => state.Entries.All(e => e.Name != key)))
+ // Clear all existing entries
+ foreach (var entry in _companyEntries.Values)
{
- _companyEntries[key].Container.RemoveAllChildren();
- _companyEntries.Remove(key);
+ entry.Container.RemoveAllChildren();
}
+ _companyEntries.Clear();
+ Entries.RemoveAllChildren();
- // Update or add new entries
- foreach (var company in state.Entries)
+ // Add new entries
+ for (var i = 0; i < state.Entries.Count; i++)
{
- if (!_companyEntries.TryGetValue(company.Name, out var entry))
- {
- entry = new CompanyEntry(company.Name, OnBuyButtonPressed, OnSellButtonPressed);
- _companyEntries[company.Name] = entry;
- Entries.AddChild(entry.Container);
- }
+ var company = state.Entries[i];
+ var entry = new CompanyEntry(i, company.LocalizedDisplayName, OnBuyButtonPressed, OnSellButtonPressed);
+ _companyEntries[i] = entry;
+ Entries.AddChild(entry.Container);
- var ownedStocks = state.OwnedStocks.GetValueOrDefault(company.Name, 0);
+ var ownedStocks = state.OwnedStocks.GetValueOrDefault(i, 0);
entry.Update(company, ownedStocks);
}
}
@@ -68,12 +68,11 @@ private sealed class CompanyEntry
private readonly LineEdit _amountEdit;
private readonly PriceHistoryTable _priceHistory;
- public CompanyEntry(string companyName,
- Action? onBuyPressed,
- Action? onSellPressed)
+ public CompanyEntry(int companyIndex,
+ string displayName,
+ Action? onBuyPressed,
+ Action? onSellPressed)
{
- var companyName1 = companyName;
-
Container = new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
@@ -101,7 +100,7 @@ public CompanyEntry(string companyName,
_nameLabel = new Label
{
HorizontalExpand = true,
- Text = companyName,
+ Text = displayName,
};
// Create a panel for price and change
@@ -163,13 +162,13 @@ public CompanyEntry(string companyName,
_sharesLabel = new Label
{
- Text = "Owned: 0",
+ Text = Loc.GetString("stock-trading-owned-shares"),
MinWidth = 100,
};
_amountEdit = new LineEdit
{
- PlaceHolder = "Amount",
+ PlaceHolder = Loc.GetString("stock-trading-amount-placeholder"),
HorizontalExpand = true,
MinWidth = 80,
};
@@ -183,14 +182,14 @@ public CompanyEntry(string companyName,
_buyButton = new Button
{
- Text = "Buy",
+ Text = Loc.GetString("stock-trading-buy-button"),
MinWidth = 65,
Margin = new Thickness(3, 0, 3, 0),
};
_sellButton = new Button
{
- Text = "Sell",
+ Text = Loc.GetString("stock-trading-sell-button"),
MinWidth = 65,
};
@@ -218,13 +217,13 @@ public CompanyEntry(string companyName,
_buyButton.OnPressed += _ =>
{
if (float.TryParse(_amountEdit.Text, out var amount) && amount > 0)
- onBuyPressed?.Invoke(companyName1, amount);
+ onBuyPressed?.Invoke(companyIndex, amount);
};
_sellButton.OnPressed += _ =>
{
if (float.TryParse(_amountEdit.Text, out var amount) && amount > 0)
- onSellPressed?.Invoke(companyName1, amount);
+ onSellPressed?.Invoke(companyIndex, amount);
};
// There has to be a better way of doing this
@@ -238,12 +237,12 @@ public CompanyEntry(string companyName,
public void Update(StockCompanyStruct company, int ownedStocks)
{
- _nameLabel.Text = company.DisplayName;
+ _nameLabel.Text = company.LocalizedDisplayName;
_priceLabel.Text = $"${company.CurrentPrice:F2}";
- _sharesLabel.Text = $"Owned: {ownedStocks}";
+ _sharesLabel.Text = Loc.GetString("stock-trading-owned-shares", ("shares", ownedStocks));
var priceChange = 0f;
- if (company.PriceHistory.Count > 0)
+ if (company.PriceHistory is { Count: > 0 })
{
var previousPrice = company.PriceHistory[^1];
priceChange = (company.CurrentPrice - previousPrice) / previousPrice * 100;
@@ -259,8 +258,9 @@ public void Update(StockCompanyStruct company, int ownedStocks)
_ => NeutralColor,
};
- // Update the price history table
- _priceHistory.Update(company.PriceHistory);
+ // Update the price history table if not null
+ if (company.PriceHistory != null)
+ _priceHistory.Update(company.PriceHistory);
// Disable sell button if no shares owned
_sellButton.Disabled = ownedStocks <= 0;
diff --git a/Content.Server/DeltaV/Cargo/Components/StationStockMarketComponent.cs b/Content.Server/DeltaV/Cargo/Components/StationStockMarketComponent.cs
index 617fa6f441d5..4ea9bd431330 100644
--- a/Content.Server/DeltaV/Cargo/Components/StationStockMarketComponent.cs
+++ b/Content.Server/DeltaV/Cargo/Components/StationStockMarketComponent.cs
@@ -1,4 +1,6 @@
using System.Numerics;
+using Content.Server.DeltaV.Cargo.Systems;
+using Content.Server.DeltaV.CartridgeLoader.Cartridges;
using Content.Shared.CartridgeLoader.Cartridges;
using Robust.Shared.Audio;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
@@ -7,19 +9,20 @@
namespace Content.Server.DeltaV.Cargo.Components;
[RegisterComponent, AutoGenerateComponentPause]
+[Access(typeof(StockMarketSystem), typeof(StockTradingCartridgeSystem))]
public sealed partial class StationStockMarketComponent : Component
{
///
/// The list of companies you can invest in
///
[DataField]
- public Dictionary Companies = new();
+ public List Companies = [];
///
/// The list of shares owned by the station
///
[DataField]
- public Dictionary StockOwnership = new();
+ public Dictionary StockOwnership = new();
///
/// The interval at which the stock market updates
@@ -46,51 +49,23 @@ public sealed partial class StationStockMarketComponent : Component
[DataField]
public SoundSpecifier DenySound = new SoundPathSpecifier("/Audio/Effects/Cargo/buzz_sigh.ogg");
- ///
- /// The chance for minor market changes
- ///
- [DataField]
- public float MinorChangeChance = 0.86f; // 86%
-
- ///
- /// The chance for moderate market changes
- ///
- [DataField]
- public float ModerateChangeChance = 0.10f; // 10%
-
- ///
- /// The chance for major market changes
- ///
- [DataField]
- public float MajorChangeChance = 0.03f; // 3%
-
- ///
- /// The chance for catastrophic market changes
- ///
- [DataField]
- public float CatastrophicChangeChance = 0.01f; // 1%
-
- ///
- /// The price range for minor changes
- ///
- [DataField]
- public Vector2 MinorChangeRange = new(-0.05f, 0.05f); // -5% to +5%
-
- ///
- /// The price range for moderate changes
- ///
+ // These work well as presets but can be changed in the yaml
[DataField]
- public Vector2 ModerateChangeRange = new(-0.3f, 0.2f); // -30% to +20%
+ public List MarketChanges =
+ [
+ new() { Chance = 0.86f, Range = new Vector2(-0.05f, 0.05f) }, // Minor
+ new() { Chance = 0.10f, Range = new Vector2(-0.3f, 0.2f) }, // Moderate
+ new() { Chance = 0.03f, Range = new Vector2(-0.5f, 1.5f) }, // Major
+ new() { Chance = 0.01f, Range = new Vector2(-0.9f, 4.0f) }, // Catastrophic
+ ];
+}
- ///
- /// The price range for major changes
- ///
- [DataField]
- public Vector2 MajorChangeRange = new(-0.5f, 1.5f); // -50% to +150%
+[DataDefinition]
+public sealed partial class MarketChange
+{
+ [DataField(required: true)]
+ public float Chance;
- ///
- /// The price range for catastrophic changes
- ///
- [DataField]
- public Vector2 CatastrophicChangeRange = new(-0.9f, 4.0f); // -90% to +400%
+ [DataField(required: true)]
+ public Vector2 Range;
}
diff --git a/Content.Server/DeltaV/Cargo/StocksCommands.cs b/Content.Server/DeltaV/Cargo/StocksCommands.cs
index 9466281fb11f..dfe1776f6662 100644
--- a/Content.Server/DeltaV/Cargo/StocksCommands.cs
+++ b/Content.Server/DeltaV/Cargo/StocksCommands.cs
@@ -17,34 +17,59 @@ public sealed class ChangeStocksPriceCommand : IConsoleCommand
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
- public async void Execute(IConsoleShell shell, string argStr, string[] args)
+ public void Execute(IConsoleShell shell, string argStr, string[] args)
{
- if (args.Length != 2)
+ if (args.Length < 2)
{
shell.WriteLine(Loc.GetString("shell-wrong-arguments-number"));
return;
}
+ if (!int.TryParse(args[0], out var companyIndex))
+ {
+ shell.WriteError(Loc.GetString("shell-argument-must-be-number"));
+ return;
+ }
+
if (!float.TryParse(args[1], out var newPrice))
{
shell.WriteError(Loc.GetString("shell-argument-must-be-number"));
return;
}
- var name = args[0];
+ EntityUid? targetStation = null;
+ if (args.Length > 2)
+ {
+ if (!EntityUid.TryParse(args[2], out var station))
+ {
+ shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
+ return;
+ }
+ targetStation = station;
+ }
var stockMarket = _entitySystemManager.GetEntitySystem();
-
var query = _entityManager.EntityQueryEnumerator();
+
while (query.MoveNext(out var uid, out var comp))
{
- if (stockMarket.TryChangeStocksPrice(uid, comp, newPrice, name))
+ // Skip if we're looking for a specific station and this isn't it
+ if (targetStation != null && uid != targetStation)
continue;
+
+ if (stockMarket.TryChangeStocksPrice(uid, comp, newPrice, companyIndex))
+ {
+ shell.WriteLine(Loc.GetString("shell-command-success"));
+ return;
+ }
+
shell.WriteLine(Loc.GetString("cmd-changestocksprice-invalid-company"));
return;
}
- shell.WriteLine(Loc.GetString("shell-command-success"));
+ shell.WriteLine(targetStation != null
+ ? Loc.GetString("cmd-changestocksprice-invalid-station")
+ : Loc.GetString("cmd-changestocksprice-no-stations"));
}
}
@@ -58,43 +83,53 @@ public sealed class AddStocksCompanyCommand : IConsoleCommand
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
- public async void Execute(IConsoleShell shell, string argStr, string[] args)
+ public void Execute(IConsoleShell shell, string argStr, string[] args)
{
- if (args.Length != 3)
+ if (args.Length < 2)
{
shell.WriteLine(Loc.GetString("shell-wrong-arguments-number"));
return;
}
- if (!float.TryParse(args[2], out var basePrice))
+ if (!float.TryParse(args[1], out var basePrice))
{
shell.WriteError(Loc.GetString("shell-argument-must-be-number"));
return;
}
- var name = args[0];
- var displayName = args[1];
-
- var company = new StockCompanyStruct
+ EntityUid? targetStation = null;
+ if (args.Length > 2)
{
- Name = name,
- DisplayName = displayName,
- CurrentPrice = basePrice,
- BasePrice = basePrice,
- PriceHistory = [],
- };
+ if (!EntityUid.TryParse(args[2], out var station))
+ {
+ shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
+ return;
+ }
+ targetStation = station;
+ }
+ var displayName = args[0];
var stockMarket = _entitySystemManager.GetEntitySystem();
-
var query = _entityManager.EntityQueryEnumerator();
+
while (query.MoveNext(out var uid, out var comp))
{
- if (stockMarket.TryAddCompany(uid, comp, company))
+ // Skip if we're looking for a specific station and this isn't it
+ if (targetStation != null && uid != targetStation)
continue;
+
+ if (stockMarket.TryAddCompany(uid, comp, basePrice, displayName))
+ {
+ shell.WriteLine(Loc.GetString("shell-command-success"));
+ return;
+ }
+
shell.WriteLine(Loc.GetString("cmd-addstockscompany-failure"));
return;
}
- shell.WriteLine(Loc.GetString("shell-command-success"));
+ shell.WriteLine(targetStation != null
+ ? Loc.GetString("cmd-addstockscompany-invalid-station")
+ : Loc.GetString("cmd-addstockscompany-no-stations"));
}
}
diff --git a/Content.Server/DeltaV/Cargo/Systems/StockMarketSystem.cs b/Content.Server/DeltaV/Cargo/Systems/StockMarketSystem.cs
index f1e93f0fdb54..5ff5cd4ff7fd 100644
--- a/Content.Server/DeltaV/Cargo/Systems/StockMarketSystem.cs
+++ b/Content.Server/DeltaV/Cargo/Systems/StockMarketSystem.cs
@@ -1,4 +1,3 @@
-using System.Linq;
using Content.Server.Access.Systems;
using Content.Server.Administration.Logs;
using Content.Server.Cargo.Components;
@@ -43,15 +42,6 @@ public override void Initialize()
_sawmill = _log.GetSawmill("admin.stock_market");
SubscribeLocalEvent(OnStockTradingMessage);
- SubscribeLocalEvent(OnInit);
- }
-
- private static void OnInit(Entity ent, ref ComponentInit args)
- {
- foreach (var company in ent.Comp.Companies.Values)
- {
- UpdatePriceHistory(company);
- }
}
public override void Update(float frameTime)
@@ -74,8 +64,8 @@ private void OnStockTradingMessage(Entity ent, r
if (args is not StockTradingUiMessageEvent message)
return;
- var name = message.Company;
- var amount = (int)message.Amount; // Convert to int since we can't have partial shares
+ var companyIndex = message.CompanyIndex;
+ var amount = (int)message.Amount;
var station = ent.Comp.Station;
var loader = GetEntity(args.LoaderUid);
var xform = Transform(loader);
@@ -84,7 +74,10 @@ private void OnStockTradingMessage(Entity ent, r
if (station == null || !TryComp(station, out var stockMarket))
return;
- // Check for access reader component
+ // Validate company index
+ if (companyIndex < 0 || companyIndex >= stockMarket.Companies.Count)
+ return;
+
if (!TryComp(ent.Owner, out var access))
return;
@@ -108,9 +101,7 @@ private void OnStockTradingMessage(Entity ent, r
try
{
- // Ensure the specified company exists
- if (!stockMarket.Companies.TryGetValue(name, out var company))
- return;
+ var company = stockMarket.Companies[companyIndex];
// Attempt to buy or sell stocks based on the action
bool success;
@@ -119,15 +110,15 @@ private void OnStockTradingMessage(Entity ent, r
case StockTradingUiAction.Buy:
_adminLogger.Add(LogType.Action,
LogImpact.Medium,
- $"{ToPrettyString(loader)} attempting to buy {amount} stocks of {company.Name}");
- success = TryBuyStocks(station.Value, stockMarket, name, amount);
+ $"{ToPrettyString(loader)} attempting to buy {amount} stocks of {company.LocalizedDisplayName}");
+ success = TryBuyStocks(station.Value, stockMarket, companyIndex, amount);
break;
case StockTradingUiAction.Sell:
_adminLogger.Add(LogType.Action,
LogImpact.Medium,
- $"{ToPrettyString(loader)} attempting to sell {amount} stocks of {company.Name}");
- success = TrySellStocks(station.Value, stockMarket, name, amount);
+ $"{ToPrettyString(loader)} attempting to sell {amount} stocks of {company.LocalizedDisplayName}");
+ success = TrySellStocks(station.Value, stockMarket, companyIndex, amount);
break;
default:
@@ -157,40 +148,34 @@ private void OnStockTradingMessage(Entity ent, r
private bool TryBuyStocks(
EntityUid station,
StationStockMarketComponent stockMarket,
- string companyKey,
+ int companyIndex,
int amount)
{
- if (amount <= 0)
+ if (amount <= 0 || companyIndex < 0 || companyIndex >= stockMarket.Companies.Count)
return false;
// Check if the station has a bank account
if (!TryComp(station, out var bank))
return false;
- // Check if the company exists
- if (!stockMarket.Companies.TryGetValue(companyKey, out var company))
- return false;
-
- // Convert to int
+ var company = stockMarket.Companies[companyIndex];
var totalValue = (int)Math.Round(company.CurrentPrice * amount);
// See if we can afford it
if (bank.Balance < totalValue)
return false;
- // Update stock ownership
- if (!stockMarket.StockOwnership.TryGetValue(companyKey, out var currentOwned))
+ if (!stockMarket.StockOwnership.TryGetValue(companyIndex, out var currentOwned))
currentOwned = 0;
// Update the bank account
_cargo.UpdateBankAccount(station, bank, -totalValue);
-
- stockMarket.StockOwnership[companyKey] = currentOwned + amount;
+ stockMarket.StockOwnership[companyIndex] = currentOwned + amount;
// Log the transaction
_adminLogger.Add(LogType.Action,
LogImpact.Medium,
- $"[StockMarket] Bought {amount} stocks of {company.Name} at {company.CurrentPrice:F2} credits each (Total: {totalValue})");
+ $"[StockMarket] Bought {amount} stocks of {company.LocalizedDisplayName} at {company.CurrentPrice:F2} credits each (Total: {totalValue})");
return true;
}
@@ -198,33 +183,28 @@ private bool TryBuyStocks(
private bool TrySellStocks(
EntityUid station,
StationStockMarketComponent stockMarket,
- string companyKey,
+ int companyIndex,
int amount)
{
- if (amount <= 0)
+ if (amount <= 0 || companyIndex < 0 || companyIndex >= stockMarket.Companies.Count)
return false;
// Check if the station has a bank account
if (!TryComp(station, out var bank))
return false;
- // Check if the company exists
- if (!stockMarket.Companies.TryGetValue(companyKey, out var company))
- return false;
-
- // Check if the station owns enough stocks
- if (!stockMarket.StockOwnership.TryGetValue(companyKey, out var currentOwned) || currentOwned < amount)
+ if (!stockMarket.StockOwnership.TryGetValue(companyIndex, out var currentOwned) || currentOwned < amount)
return false;
- // Convert to int
+ var company = stockMarket.Companies[companyIndex];
var totalValue = (int)Math.Round(company.CurrentPrice * amount);
// Update stock ownership
var newAmount = currentOwned - amount;
if (newAmount > 0)
- stockMarket.StockOwnership[companyKey] = newAmount;
+ stockMarket.StockOwnership[companyIndex] = newAmount;
else
- stockMarket.StockOwnership.Remove(companyKey);
+ stockMarket.StockOwnership.Remove(companyIndex);
// Update the bank account
_cargo.UpdateBankAccount(station, bank, totalValue);
@@ -232,20 +212,18 @@ private bool TrySellStocks(
// Log the transaction
_adminLogger.Add(LogType.Action,
LogImpact.Medium,
- $"[StockMarket] Sold {amount} stocks of {company.Name} at {company.CurrentPrice:F2} credits each (Total: {totalValue})");
+ $"[StockMarket] Sold {amount} stocks of {company.LocalizedDisplayName} at {company.CurrentPrice:F2} credits each (Total: {totalValue})");
return true;
}
private void UpdateStockPrices(EntityUid station, StationStockMarketComponent stockMarket)
{
- var companies = stockMarket.Companies;
-
- foreach (var key in companies.Keys.ToList())
+ for (var i = 0; i < stockMarket.Companies.Count; i++)
{
- var company = companies[key];
- var changeType = DetermineChangeType(stockMarket);
- var multiplier = CalculatePriceMultiplier(changeType, stockMarket);
+ var company = stockMarket.Companies[i];
+ var changeType = DetermineMarketChange(stockMarket.MarketChanges);
+ var multiplier = CalculatePriceMultiplier(changeType);
UpdatePriceHistory(company);
@@ -259,8 +237,7 @@ private void UpdateStockPrices(EntityUid station, StationStockMarketComponent st
// Ensure price doesn't go above maximum threshold
company.CurrentPrice = MathF.Min(company.CurrentPrice, MaxPrice);
- // Save the modified struct back to the dictionary
- companies[key] = company;
+ stockMarket.Companies[i] = company;
// Calculate the percentage change
var percentChange = (company.CurrentPrice - oldPrice) / oldPrice * 100;
@@ -272,7 +249,7 @@ private void UpdateStockPrices(EntityUid station, StationStockMarketComponent st
// Log it
_adminLogger.Add(LogType.Action,
LogImpact.Medium,
- $"[StockMarket] Company '{company.Name}' price updated by {percentChange:+0.00;-0.00}% from {oldPrice:0.00} to {company.CurrentPrice:0.00}");
+ $"[StockMarket] Company '{company.LocalizedDisplayName}' price updated by {percentChange:+0.00;-0.00}% from {oldPrice:0.00} to {company.CurrentPrice:0.00}");
}
}
@@ -283,7 +260,7 @@ private void UpdateStockPrices(EntityUid station, StationStockMarketComponent st
public bool TryChangeStocksPrice(EntityUid station,
StationStockMarketComponent stockMarket,
float newPrice,
- string companyName)
+ int companyIndex)
{
// Check if it exceeds the max price
if (newPrice > MaxPrice)
@@ -292,31 +269,18 @@ public bool TryChangeStocksPrice(EntityUid station,
return false;
}
- var companies = stockMarket.Companies;
- foreach (var key in companies.Keys.ToList())
- {
- var company = companies[key];
-
- // Continue if it doesn't match the company we're looking for
- if (company.Name != companyName)
- continue;
-
- UpdatePriceHistory(company);
-
- // Update price
- company.CurrentPrice = newPrice;
+ if (companyIndex < 0 || companyIndex >= stockMarket.Companies.Count)
+ return false;
- // Ensure it doesn't go below minimum threshold
- company.CurrentPrice = MathF.Max(company.CurrentPrice, company.BasePrice * 0.1f);
+ var company = stockMarket.Companies[companyIndex];
+ UpdatePriceHistory(company);
- // Save the modified struct back to the dictionary
- companies[key] = company;
+ company.CurrentPrice = MathF.Max(newPrice, company.BasePrice * 0.1f);
+ stockMarket.Companies[companyIndex] = company;
- var ev = new StockMarketUpdatedEvent(station);
- RaiseLocalEvent(ev);
- return true;
- }
- return false;
+ var ev = new StockMarketUpdatedEvent(station);
+ RaiseLocalEvent(ev);
+ return true;
}
///
@@ -326,34 +290,24 @@ public bool TryChangeStocksPrice(EntityUid station,
public bool TryAddCompany(EntityUid station,
StationStockMarketComponent stockMarket,
float basePrice,
- string companyName,
string displayName)
{
- var companies = stockMarket.Companies;
-
- // Check if the company already exists in the dictionary
- if (companies.ContainsKey(companyName))
- {
- return false;
- }
-
- // Create a new company struct with the specified parameters
- var company = new StockCompanyStruct
- {
- Name = companyName,
- DisplayName = displayName,
- BasePrice = basePrice,
- CurrentPrice = basePrice,
- PriceHistory = [],
- };
+ // Create a new company struct with the specified parameters
+ var company = new StockCompanyStruct
+ {
+ LocalizedDisplayName = displayName, // Assume there's no Loc for it
+ BasePrice = basePrice,
+ CurrentPrice = basePrice,
+ PriceHistory = [],
+ };
- // Add the new company to the dictionary
- companies[companyName] = company;
+ stockMarket.Companies.Add(company);
+ UpdatePriceHistory(company);
- var ev = new StockMarketUpdatedEvent(station);
- RaiseLocalEvent(ev);
+ var ev = new StockMarketUpdatedEvent(station);
+ RaiseLocalEvent(ev);
- return true;
+ return true;
}
///
@@ -364,11 +318,11 @@ public bool TryAddCompany(EntityUid station,
StationStockMarketComponent stockMarket,
StockCompanyStruct company)
{
- var companies = stockMarket.Companies;
-
// Add the new company to the dictionary
- if (!companies.TryAdd(company.Name, company))
- return false;
+ stockMarket.Companies.Add(company);
+
+ // Make sure it has a price history
+ UpdatePriceHistory(company);
var ev = new StockMarketUpdatedEvent(station);
RaiseLocalEvent(ev);
@@ -378,13 +332,15 @@ public bool TryAddCompany(EntityUid station,
private static void UpdatePriceHistory(StockCompanyStruct company)
{
+ // Create if null
+ company.PriceHistory ??= [];
+
// Make sure it has at least 5 entries
while (company.PriceHistory.Count < 5)
{
company.PriceHistory.Add(company.BasePrice);
}
-
// Store previous price in history
company.PriceHistory.Add(company.CurrentPrice);
@@ -392,57 +348,35 @@ private static void UpdatePriceHistory(StockCompanyStruct company)
company.PriceHistory.RemoveAt(1); // Always keep the base price
}
- private StockChangeType DetermineChangeType(StationStockMarketComponent stockMarket)
+ private MarketChange DetermineMarketChange(List marketChanges)
{
var roll = _random.NextFloat();
+ var cumulative = 0f;
- if (roll < stockMarket.CatastrophicChangeChance)
- return StockChangeType.Catastrophic;
-
- roll -= stockMarket.CatastrophicChangeChance;
-
- if (roll < stockMarket.MajorChangeChance)
- return StockChangeType.Major;
-
- roll -= stockMarket.MajorChangeChance;
-
- if (roll < stockMarket.ModerateChangeChance)
- return StockChangeType.Moderate;
+ foreach (var change in marketChanges)
+ {
+ cumulative += change.Chance;
+ if (roll <= cumulative)
+ return change;
+ }
- return StockChangeType.Minor;
+ return marketChanges[0]; // Default to first (usually minor) change if we somehow exceed 100%
}
- private float CalculatePriceMultiplier(StockChangeType changeType, StationStockMarketComponent stockMarket)
+ private float CalculatePriceMultiplier(MarketChange change)
{
- var (min, max) = changeType switch
- {
- StockChangeType.Minor => stockMarket.MinorChangeRange,
- StockChangeType.Moderate => stockMarket.ModerateChangeRange,
- StockChangeType.Major => stockMarket.MajorChangeRange,
- StockChangeType.Catastrophic => stockMarket.CatastrophicChangeRange,
- _ => throw new ArgumentOutOfRangeException(nameof(changeType)),
- };
-
// Using Box-Muller transform for normal distribution
var u1 = _random.NextFloat();
var u2 = _random.NextFloat();
var randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2);
// Scale and shift the result to our desired range
- var range = max - min;
- var mean = (max + min) / 2;
+ var range = change.Range.Y - change.Range.X;
+ var mean = (change.Range.Y + change.Range.X) / 2;
var stdDev = range / 6.0f; // 99.7% of values within range
var result = (float)(mean + (stdDev * randStdNormal));
- return Math.Clamp(result, min, max);
- }
-
- private enum StockChangeType
- {
- Minor,
- Moderate,
- Major,
- Catastrophic,
+ return Math.Clamp(result, change.Range.X, change.Range.Y);
}
}
public sealed class StockMarketUpdatedEvent(EntityUid station) : EntityEventArgs
diff --git a/Content.Server/DeltaV/CartridgeLoader/Cartridges/StockTradingCartridgeSystem.cs b/Content.Server/DeltaV/CartridgeLoader/Cartridges/StockTradingCartridgeSystem.cs
index fddd996d2953..cd68c5adb43a 100644
--- a/Content.Server/DeltaV/CartridgeLoader/Cartridges/StockTradingCartridgeSystem.cs
+++ b/Content.Server/DeltaV/CartridgeLoader/Cartridges/StockTradingCartridgeSystem.cs
@@ -1,3 +1,4 @@
+using System.Linq;
using Content.Server.Cargo.Components;
using Content.Server.DeltaV.Cargo.Components;
using Content.Server.DeltaV.Cargo.Systems;
@@ -39,9 +40,24 @@ private void OnStockMarketUpdated(StockMarketUpdatedEvent args)
UpdateAllCartridges(args.Station);
}
- private void OnMapInit(EntityUid uid, StationStockMarketComponent stockMarket, MapInitEvent args)
+ private void OnMapInit(Entity ent, ref MapInitEvent args)
{
- if (_station.GetOwningStation(uid) is { } station)
+ // Initialize price history for each company
+ for (var i = 0; i < ent.Comp.Companies.Count; i++)
+ {
+ var company = ent.Comp.Companies[i];
+
+ // Create initial price history using base price
+ company.PriceHistory = new List();
+ for (var j = 0; j < 5; j++)
+ {
+ company.PriceHistory.Add(company.BasePrice);
+ }
+
+ ent.Comp.Companies[i] = company;
+ }
+
+ if (_station.GetOwningStation(ent.Owner) is { } station)
UpdateAllCartridges(station);
}
@@ -66,18 +82,12 @@ private void UpdateUI(Entity ent, EntityUid load
return;
// Convert company data to UI state format
- var entries = new List();
-
- foreach (var (key, company) in stockMarket.Companies)
- {
- entries.Add(new StockCompanyStruct(
- name: company.Name,
- displayName: company.DisplayName,
+ var entries = stockMarket.Companies.Select(company => new StockCompanyStruct(
+ displayName: company.LocalizedDisplayName,
currentPrice: company.CurrentPrice,
basePrice: company.BasePrice,
- priceHistory: company.PriceHistory
- ));
- }
+ priceHistory: company.PriceHistory))
+ .ToList();
// Send the UI state with balance and owned stocks
var state = new StockTradingUiState(
diff --git a/Content.Shared/DeltaV/CartridgeLoader/Cartridges/StockTradingUiMessageEvent.cs b/Content.Shared/DeltaV/CartridgeLoader/Cartridges/StockTradingUiMessageEvent.cs
index cc36a87bbb36..a80f8c6b8a88 100644
--- a/Content.Shared/DeltaV/CartridgeLoader/Cartridges/StockTradingUiMessageEvent.cs
+++ b/Content.Shared/DeltaV/CartridgeLoader/Cartridges/StockTradingUiMessageEvent.cs
@@ -3,18 +3,12 @@
namespace Content.Shared.CartridgeLoader.Cartridges;
[Serializable, NetSerializable]
-public sealed class StockTradingUiMessageEvent : CartridgeMessageEvent
+public sealed class StockTradingUiMessageEvent(StockTradingUiAction action, int companyIndex, float amount)
+ : CartridgeMessageEvent
{
- public readonly StockTradingUiAction Action;
- public readonly string Company;
- public readonly float Amount;
-
- public StockTradingUiMessageEvent(StockTradingUiAction action, string company, float amount)
- {
- Action = action;
- Company = company;
- Amount = amount;
- }
+ public readonly StockTradingUiAction Action = action;
+ public readonly int CompanyIndex = companyIndex;
+ public readonly float Amount = amount;
}
[Serializable, NetSerializable]
diff --git a/Content.Shared/DeltaV/CartridgeLoader/Cartridges/StockTradingUiState.cs b/Content.Shared/DeltaV/CartridgeLoader/Cartridges/StockTradingUiState.cs
index 0f0bafb9a59d..aea4ba5aa1db 100644
--- a/Content.Shared/DeltaV/CartridgeLoader/Cartridges/StockTradingUiState.cs
+++ b/Content.Shared/DeltaV/CartridgeLoader/Cartridges/StockTradingUiState.cs
@@ -5,12 +5,12 @@ namespace Content.Shared.CartridgeLoader.Cartridges;
[Serializable, NetSerializable]
public sealed class StockTradingUiState(
List entries,
- Dictionary ownedStocks,
+ Dictionary ownedStocks,
float balance)
: BoundUserInterfaceState
{
public readonly List Entries = entries;
- public readonly Dictionary OwnedStocks = ownedStocks;
+ public readonly Dictionary OwnedStocks = ownedStocks;
public readonly float Balance = balance;
}
@@ -19,16 +19,23 @@ public sealed class StockTradingUiState(
public partial struct StockCompanyStruct
{
///
- /// The internal name/key of the company. Should not contain spaces or special characters.
+ /// The displayed name of the company shown in the UI.
///
[DataField(required: true)]
- public string Name;
+ public LocId? DisplayName;
+
+ // Used for runtime-added companies that don't have a localization entry
+ private string? _displayName;
///
- /// The displayed name of the company shown in the UI. Can contain spaces and special characters.
+ /// Gets or sets the display name, using either the localized or direct string value
///
- [DataField(required: true)]
- public string DisplayName;
+ [Access(Other = AccessPermissions.ReadWriteExecute)]
+ public string LocalizedDisplayName
+ {
+ get => _displayName ?? Loc.GetString(DisplayName ?? string.Empty);
+ set => _displayName = value;
+ }
///
/// The current price of the company's stock
@@ -46,14 +53,14 @@ public partial struct StockCompanyStruct
/// The price history of the company's stock
///
[DataField]
- public List PriceHistory;
+ public List? PriceHistory;
- public StockCompanyStruct(string name, string displayName, float currentPrice, float basePrice, List priceHistory)
+ public StockCompanyStruct(string displayName, float currentPrice, float basePrice, List? priceHistory)
{
- Name = name;
DisplayName = displayName;
+ _displayName = null;
CurrentPrice = currentPrice;
BasePrice = basePrice;
- PriceHistory = priceHistory;
+ PriceHistory = priceHistory ?? [];
}
}
diff --git a/Resources/Locale/en-US/deltav/cargo/stocks-commands.ftl b/Resources/Locale/en-US/deltav/cargo/stocks-commands.ftl
index 6bd2cf52cfb5..8e0fe014999e 100644
--- a/Resources/Locale/en-US/deltav/cargo/stocks-commands.ftl
+++ b/Resources/Locale/en-US/deltav/cargo/stocks-commands.ftl
@@ -1,6 +1,13 @@
+# changestockprice command
cmd-changestocksprice-desc = Changes a company's stock price to the specified number.
-cmd-changestocksprice-help = changestockprice
-cmd-changestocksprice-failure = Failed to execute command! Invalid company name or the new price exceeds the allowed limit.
+cmd-changestocksprice-help = changestockprice [Station UID]
+cmd-changestocksprice-invalid-company = Failed to execute command! Invalid company index or the new price exceeds the allowed limit.
+cmd-changestocksprice-invalid-station = No stock market found for specified station
+cmd-changestocksprice-no-stations = No stations with stock markets found
+
+# addstockscompany command
cmd-addstockscompany-desc = Adds a new company to the stocks market.
-cmd-addstockscompany-help = addstockscompany
-cmd-addstockscompany-already-exists = Failed to execute command! Are you sure the company doesn't exist already?
+cmd-addstockscompany-help = addstockscompany [Station UID]
+cmd-addstockscompany-failure = Failed to add company to the stock market.
+cmd-addstockscompany-invalid-station = No stock market found for specified station
+cmd-addstockscompany-no-stations = No stations with stock markets found
diff --git a/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl b/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl
index 56ba93ea82da..bc07e0f4f1a7 100644
--- a/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl
+++ b/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl
@@ -1,3 +1,6 @@
+## CrimeAssist
+
+# General
crime-assist-program-name = Crime Assist
crime-assist-yes-button = Yes
crime-assist-no-button = No
@@ -6,6 +9,14 @@ crime-assist-crimetype-misdemeanour = Misdemeanour
crime-assist-crimetype-felony = Felony
crime-assist-crimetype-capital = Capital
crime-assist-crime-innocent = No crime was committed
+crime-assist-mainmenu = Welcome to Crime Assist!
+crime-assist-sophont-explanation = A sophont is described as any entity with the capacity to display the following attributes:
+ • [bold]Sapience[/bold]: the entity possesses basic logic and problem-solving skills, or at a minimum some level of significant intelligence.
+ • [bold]Sentience[/bold]: the entity has the capacity to process an emotion or lack thereof, or at a minimum the ability to recognise its own pain.
+ • [bold]Self-awareness[/bold]: the entity is capable of altering its behaviour in a reasonable fashion as a result of stimuli, or at a minimum is capable of recognising its own sapience and sentience.
+ Any sophont is considered a legal person, regardless of origin or prior cognitive status. Much like any other intelligent organic, a sophont may press charges against crew and be tried for crimes.
+
+# Crimes
crime-assist-crime-animalcruelty = Code 101: Animal Cruelty
crime-assist-crime-theft = Code 102: Theft
crime-assist-crime-trespass = Code 110: Trespass
@@ -32,7 +43,8 @@ crime-assist-crime-decorporealisation = Code 305: Decorporealisation
crime-assist-crime-kidnapping = Code 309: Kidnapping
crime-assist-crime-sedition = Code 311: Sedition
crime-assist-crime-sexualharassment = Code 314: Sexual Harassment
-crime-assist-mainmenu = Welcome to Crime Assist!
+
+# Questions
crime-assist-question-isitterrorism = Did the suspect hold hostages, cause many deaths or major destruction to force compliance from the crew?
crime-assist-question-wassomeoneattacked = Was an entity attacked?
crime-assist-question-wasitsophont = Was the victim in question a sophont?
@@ -59,6 +71,8 @@ crime-assist-question-happenincourt = Was the suspect a nuisance in court?
crime-assist-question-duringactiveinvestigation = Was the suspect a nuisance during an active investigation, and hindered the investigation as a result?
crime-assist-question-tocommandstaff = Did the suspect overthrow or compromise a lawfully established Chain of Command, or attempt to do so?
crime-assist-question-wasitcommanditself = Was a command staff or department head abusing authority over another sophont?
+
+# Crime details
crime-assist-crimedetail-innocent = Crime could not be determined. Use your best judgement to resolve the situation.
crime-assist-crimedetail-animalcruelty = To inflict unnecessary suffering on a sapient being with malicious intent.
crime-assist-crimedetail-theft = To unlawfully take property or items without consent.
@@ -86,6 +100,8 @@ crime-assist-crimedetail-decorporealisation = To unlawfully, maliciously, and pe
crime-assist-crimedetail-kidnapping = To unlawfully confine or restrict the free movement of a sophont against their will.
crime-assist-crimedetail-sedition = To act to overthrow a lawfully established Chain of Command or governing body without lawful or legitimate cause.
crime-assist-crimedetail-sexualharassment = To sexually harass, attempt to coerce into sexual relations, or effect unwanted sexual contact with an unwilling sophont.
+
+# Punishments
crime-assist-crimepunishment-innocent = No punishment may be necessary
crime-assist-crimepunishment-animalcruelty = Punishment: 3 minutes
crime-assist-crimepunishment-theft = Punishment: 2 minutes
@@ -113,12 +129,10 @@ crime-assist-crimepunishment-decorporealisation = Punishment: Capital
crime-assist-crimepunishment-kidnapping = Punishment: Capital
crime-assist-crimepunishment-sedition = Punishment: Capital
crime-assist-crimepunishment-sexualharassment = Punishment: Capital
-crime-assist-sophont-explanation = A sophont is described as any entity with the capacity to display the following attributes:
- • [bold]Sapience[/bold]: the entity possesses basic logic and problem-solving skills, or at a minimum some level of significant intelligence.
- • [bold]Sentience[/bold]: the entity has the capacity to process an emotion or lack thereof, or at a minimum the ability to recognise its own pain.
- • [bold]Self-awareness[/bold]: the entity is capable of altering its behaviour in a reasonable fashion as a result of stimuli, or at a minimum is capable of recognising its own sapience and sentience.
- Any sophont is considered a legal person, regardless of origin or prior cognitive status. Much like any other intelligent organic, a sophont may press charges against crew and be tried for crimes.
+## MailMetrics
+
+# General
mail-metrics-program-name = MailMetrics
mail-metrics-header = Income from Mail Deliveries
mail-metrics-opened = Earnings (Opened)
@@ -131,4 +145,23 @@ mail-metrics-money-header = Spesos
mail-metrics-total = Total
mail-metrics-progress = {$opened} out of {$total} packages opened!
mail-metrics-progress-percent = Success rate: {$successRate}%
+
+## StockTrading
+
+# General
stock-trading-program-name = StockTrading
+stock-trading-title = Intergalactic Stock Market
+stock-trading-balance = Balance: {$balance} credits
+stock-trading-no-entries = No entries
+stock-trading-owned-shares = Owned: {$shares}
+stock-trading-buy-button = Buy
+stock-trading-sell-button = Sell
+stock-trading-amount-placeholder = Amount
+stock-trading-price-history = Price History
+
+# Companies
+stock-trading-company-nanotrasen = Nanotrasen [NT]
+stock-trading-company-gorlex = Gorlex [GRX]
+stock-trading-company-interdyne = Interdyne Pharmaceuticals [INTP]
+stock-trading-company-fishinc = Fish Inc. [FIN]
+stock-trading-company-donk = Donk Co. [DONK]
diff --git a/Resources/Prototypes/DeltaV/Entities/Stations/base.yml b/Resources/Prototypes/DeltaV/Entities/Stations/base.yml
index c599b18589be..4b3680a76973 100644
--- a/Resources/Prototypes/DeltaV/Entities/Stations/base.yml
+++ b/Resources/Prototypes/DeltaV/Entities/Stations/base.yml
@@ -4,58 +4,19 @@
components:
- type: StationStockMarket
companies:
- Nanotrasen:
- priceHistory:
- - 100
- - 100
- - 100
- - 100
- - 100
- basePrice: 100
- currentPrice: 100
- displayName: Nanotrasen [NT]
- name: Nanotrasen
- Gorlex:
- priceHistory:
- - 75
- - 75
- - 75
- - 75
- - 75
- basePrice: 75
- currentPrice: 75
- displayName: Gorlex [GRX]
- name: Gorlex
- Interdyne:
- priceHistory:
- - 300
- - 300
- - 300
- - 300
- - 300
- basePrice: 300
- currentPrice: 300
- displayName: Interdyne Pharmaceuticals [INTP]
- name: Interdyne
- FishInc:
- priceHistory:
- - 25
- - 25
- - 25
- - 25
- - 25
- basePrice: 25
- currentPrice: 25
- displayName: Fish Inc. [FIN]
- name: FishInc
- Donk:
- priceHistory:
- - 90
- - 90
- - 90
- - 90
- - 90
- basePrice: 90
- currentPrice: 90
- displayName: Donk Co. [DONK]
- name: Donk
+ - displayName: stock-trading-company-nanotrasen
+ basePrice: 100
+ currentPrice: 100
+ - displayName: stock-trading-company-gorlex
+ basePrice: 75
+ currentPrice: 75
+ - displayName: stock-trading-company-interdyne
+ basePrice: 300
+ currentPrice: 300
+ priceHistory: []
+ - displayName: stock-trading-company-fishinc
+ basePrice: 25
+ currentPrice: 25
+ - displayName: stock-trading-company-donk
+ basePrice: 90
+ currentPrice: 90