Stock Market UI Tweaks and improvements (#95564)

## About The Pull Request

This PR pulls forward some UI tweaks that I made in #93183, but cleaned
up and with some additional adjustments.
This adjusts a bit of the GMM Ui, cutting out the horizontal quantity
measurement and rolling that into the supply/order information in the
middle.
<img width="984" height="613" alt="image"
src="https://github.com/user-attachments/assets/40661624-c88d-4899-a624-7f3b391edf27"
/>
Adds a hit of color as well to show what materials are currently being
ordered.
Adds a bit of tooltip text in order to showcase the range of prices that
a mineral can be bought and sold at, in order to showcase why lower
value minerals like glass/iron can be bought and sold from.
In addition to this, also tweaks the logic on the disabling of buttons
to better reflect these thresholds, so you can buy and sell when sitting
on those thresholds.

## Why It's Good For The Game

The market UI needs to exist in a sort of "spreadsheet" format, in order
to see all the values, all the costs, and make decisions about what you
what to buy and how many. That said, it's clunky, and there's a lot of
complexity related to buying and selling materials already that I would
love to cut down on.

This helps trim the UI, makes it a bit smaller, but adds in some missing
information that even I don't have memorized for the purposes of regular
gameplay.

## Changelog

🆑
qol: The GMM Ui can now tell you the minimum and maximum price ranges
that a material will sell for.
qol: You can now sell materials on the edge of their buying and selling
thresholds. (This really only effects iron and glass, practically)
/🆑
This commit is contained in:
ArcaneMusic
2026-04-22 16:50:02 -04:00
committed by GitHub
parent 1889ac275f
commit 7b8efd3daa
2 changed files with 83 additions and 40 deletions
+5 -1
View File
@@ -130,6 +130,7 @@
var/sheet_to_buy
var/requested_amount
var/minimum_value_threshold = 0
var/maximum_value_threshold = 0
var/elastic_mult = 1
for(var/datum/material/traded_mat as anything in SSstock_market.materials_prices)
//convert trend into text
@@ -168,6 +169,8 @@
else
minimum_value_threshold = round(initial(traded_mat.value_per_unit) * SHEET_MATERIAL_AMOUNT * 0.5)
maximum_value_threshold = round(initial(traded_mat.value_per_unit) * SHEET_MATERIAL_AMOUNT * 3)
//Pulling elastic modifier into data.
for(var/datum/export/material/market/export_est in GLOB.exports_list)
if(export_est.material_id == traded_mat)
@@ -177,7 +180,8 @@
"name" = initial(traded_mat.name),
"price" = SSstock_market.materials_prices[traded_mat],
"rarity" = initial(traded_mat.value_per_unit),
"threshold" = minimum_value_threshold,
"min_threshold" = minimum_value_threshold,
"max_threshold" = maximum_value_threshold,
"quantity" = SSstock_market.materials_quantity[traded_mat],
"trend" = trend_string,
"color" = color_string,
+78 -39
View File
@@ -1,11 +1,13 @@
import { sortBy } from 'es-toolkit';
import {
Box,
Button,
Collapsible,
Modal,
NoticeBox,
Section,
Stack,
Tooltip,
} from 'tgui-core/components';
import { formatMoney } from 'tgui-core/format';
import type { BooleanLike } from 'tgui-core/react';
@@ -20,7 +22,8 @@ type Material = {
rarity: number;
trend: string;
price: number;
threshold: number;
min_threshold: number;
max_threshold: number;
color: string;
requested: number;
elastic: number;
@@ -56,17 +59,18 @@ export const MatMarket = (props) => {
const multiplier = orderingPrive ? 1.1 : 1;
return (
<Window width={1110} height={600}>
<Window width={990} height={615}>
<Window.Content scrollable>
{!!catastrophe && <MarketCrashModal />}
<Section
title="Materials for sale"
buttons={
!!canOrderCargo && (
<Button
icon="dollar"
<Button.Checkbox
icon={orderingPrive ? "fa-regular fa-square" : "square-check"}
tooltip="Place order from cargo budget."
color={orderingPrive ? '' : 'green'}
color={orderingPrive ? 'transparent' : 'green'}
checked={orderingPrive}
content={
orderingPrive
? 'Order via Cargo Budget?'
@@ -77,24 +81,6 @@ export const MatMarket = (props) => {
)
}
>
<NoticeBox info>
<Collapsible title="Instructions" color="blue">
Buy orders for material sheets placed here will be ordered on the
next cargo shipment.
<br /> <br />
To sell materials, please insert sheets or similar stacks of
materials. All minerals sold on the market directly are subject to
a scaling value decrease per material, but this will recover over
time. To prevent market manipulation, all registered traders can
buy a total of 10 full stacks of materials at a time.
<br /> <br />
When selling materials, prices will be decreased based on the
elastic modifier of the material, which will recover over time.
<br /> <br />
All new purchases will include the cost of the shipped crate,
which may be recycled afterwards.
</Collapsible>
</NoticeBox>
<Section>
<Stack>
<Stack.Item width="15%">
@@ -127,7 +113,7 @@ export const MatMarket = (props) => {
(material, i) => (
<Section key={i}>
<Stack fill>
<Stack.Item width="75%">
<Stack.Item width="82%">
<Stack>
<Stack.Item
textColor={material.color ? material.color : 'white'}
@@ -151,24 +137,39 @@ export const MatMarket = (props) => {
Elasticity: <b>{Math.round(material.elastic)}</b>%
</Stack.Item>
<Stack.Item width="15%" pr="2%">
Trading at <b>{formatMoney(material.price)}</b> cr.
<Stack.Item
width="15%"
pr="2%"
>
<Tooltip
content={rangeText({ minPrice: material.min_threshold, maxPrice: material.max_threshold })}
>
<u>Trading at <br /><b>{formatMoney(material.price)}</b> cr.</u>
</Tooltip>
</Stack.Item>
{material.price < material.threshold ? (
{material.price < material.min_threshold ? (
<Stack.Item width="33%" ml={2} textColor="grey">
Material price critical!
<br /> <b>Trading temporarily suspended.</b>
</Stack.Item>
) : (
<Stack.Item width="33%" ml={2}>
<b>{material.quantity || 'Zero'}</b> sheets of{' '}
<b>{material.name}</b> trading.{' '}
{material.requested || 'Zero'} sheets ordered.
<Box textColor={material.quantity === 0 ? 'grey' : 'white'}>
<b>{material.quantity || 'Zero'}</b> sheets of{' '}
<b>{material.name}</b> trading.{' '}
</Box>
<Box
bold={!!material.requested}
textColor={material.requested > 0 ? 'lightblue' : 'white'}
>
{material.requested || 'Zero'} sheets ordered.
</Box>
</Stack.Item>
)}
<Stack.Item
width="40%"
width="20%"
color={
material.trend === 'up'
? 'green'
@@ -184,9 +185,11 @@ export const MatMarket = (props) => {
</Stack.Item>
<Stack.Item>
<Button
height="35px"
verticalAlignContent="middle"
disabled={
catastrophe === 1 ||
material.price <= material.threshold ||
material.price < material.min_threshold ||
creditBalance - total_order_cost <
material.price * multiplier ||
material.requested + 1 > material.quantity
@@ -202,9 +205,11 @@ export const MatMarket = (props) => {
Buy 1
</Button>
<Button
height="35px"
verticalAlignContent="middle"
disabled={
catastrophe === 1 ||
material.price <= material.threshold ||
material.price < material.min_threshold ||
creditBalance - total_order_cost <
material.price * 5 * multiplier ||
material.requested + 5 > material.quantity
@@ -220,9 +225,11 @@ export const MatMarket = (props) => {
5
</Button>
<Button
height="35px"
verticalAlignContent="middle"
disabled={
catastrophe === 1 ||
material.price <= material.threshold ||
material.price < material.min_threshold ||
creditBalance - total_order_cost <
material.price * 10 * multiplier ||
material.requested + 10 > material.quantity
@@ -238,9 +245,11 @@ export const MatMarket = (props) => {
10
</Button>
<Button
height="35px"
verticalAlignContent="middle"
disabled={
catastrophe === 1 ||
material.price <= material.threshold ||
material.price < material.min_threshold ||
creditBalance - total_order_cost <
material.price * 25 * multiplier ||
material.requested + 25 > material.quantity
@@ -256,9 +265,11 @@ export const MatMarket = (props) => {
25
</Button>
<Button
height="35px"
verticalAlignContent="middle"
disabled={
catastrophe === 1 ||
material.price <= material.threshold ||
material.price < material.min_threshold ||
creditBalance - total_order_cost <
material.price * 50 * multiplier ||
material.requested + 50 > material.quantity
@@ -274,13 +285,28 @@ export const MatMarket = (props) => {
50
</Button>
</Stack.Item>
{material.requested > 0 && (
<Stack.Item ml={2}>x {material.requested}</Stack.Item>
)}
</Stack>
</Section>
),
)}
<NoticeBox info>
<Collapsible title="Instructions" color="blue">
Buy orders for material sheets placed here will be ordered on the
next cargo shipment.
<br /> <br />
To sell materials, please insert sheets or similar stacks of
materials. All minerals sold on the market directly are subject to
a scaling value decrease per material, but this will recover over
time. To prevent market manipulation, all registered traders can
buy a total of 10 full stacks of materials at a time.
<br /> <br />
When selling materials, prices will be decreased based on the
elastic modifier of the material, which will recover over time.
<br /> <br />
All new purchases will include the cost of the shipped crate,
which may be recycled afterwards.
</Collapsible>
</NoticeBox>
</Window.Content>
</Window>
);
@@ -299,3 +325,16 @@ const MarketCrashModal = (props) => {
</Modal>
);
};
type rangeTextProps = {
minPrice: number;
maxPrice: number;
}
function rangeText(props: rangeTextProps) {
const {minPrice, maxPrice} = props;
return (
"This material can be bought and sold between " + formatMoney(minPrice) +
" - " + formatMoney(maxPrice) + " cr."
)
}