MISC: Make TIX access independent from WSE account (#2342)

This commit is contained in:
catloversg
2025-10-12 07:28:45 +07:00
committed by GitHub
parent bd4e34fae0
commit d421d4fcf9
17 changed files with 278 additions and 67 deletions

View File

@@ -28,6 +28,7 @@ import { EntropyDev } from "./DevMenu/ui/EntropyDev";
import { Exploit } from "./Exploits/Exploit";
import { useRerender } from "./ui/React/hooks";
import { AutoExpandContext, getAutoExpandData, setAutoExpandData } from "./ui/AutoExpand/AutoExpandContext";
import { canAccessStockMarket } from "./StockMarket/StockMarket";
export function DevMenuRoot(): React.ReactElement {
const autoExpandContextValue = useRef({
@@ -65,7 +66,7 @@ export function DevMenuRoot(): React.ReactElement {
<CodingContractsDev />
{Player.hasWseAccount && <StockMarketDev />}
{canAccessStockMarket() && <StockMarketDev />}
{Player.sleeves.length > 0 && <SleevesDev />}
{Player.augmentations.some((aug) => aug.name === AugmentationName.StaneksGift1) && <StanekDev />}

View File

@@ -7,6 +7,7 @@ import {
cancelOrder,
initStockMarket,
StockMarketPromise,
isStockMarketInitialized,
} from "../StockMarket/StockMarket";
import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers";
import { StockSymbol } from "@enums";
@@ -26,9 +27,6 @@ import { getEnumHelper } from "../utils/EnumHelper";
export function NetscriptStockMarket(): InternalAPI<TIX> {
/** Checks if the player has TIX API access. Throws an error if the player does not */
const checkTixApiAccess = function (ctx: NetscriptContext): void {
if (!Player.hasWseAccount) {
throw helpers.errorMessage(ctx, `You don't have WSE Access! Cannot use ${ctx.function}()`);
}
if (!Player.hasTixApiAccess) {
throw helpers.errorMessage(ctx, `You don't have TIX API Access! Cannot use ${ctx.function}()`);
}
@@ -256,6 +254,11 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
return true;
}
if (!Player.hasWseAccount) {
helpers.log(ctx, () => "You need to have a WSE account.");
return false;
}
if (Player.money < getStockMarket4SDataCost()) {
helpers.log(ctx, () => "Not enough money to purchase 4S Market Data.");
return false;
@@ -301,7 +304,9 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
}
Player.hasWseAccount = true;
initStockMarket();
if (!isStockMarketInitialized()) {
initStockMarket();
}
Player.loseMoney(getStockMarketWseCost(), "stock");
helpers.log(ctx, () => "Purchased WSE Account Access");
return true;
@@ -318,6 +323,9 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
}
Player.hasTixApiAccess = true;
if (!isStockMarketInitialized()) {
initStockMarket();
}
Player.loseMoney(getStockMarketTixApiCost(), "stock");
helpers.log(ctx, () => "Purchased TIX API");
return true;

View File

@@ -14,7 +14,7 @@ import { resetPidCounter } from "./Netscript/Pid";
import { GetServer, AddToAllServers, initForeignServers, prestigeAllServers } from "./Server/AllServers";
import { prestigeHomeComputer } from "./Server/ServerHelpers";
import { SpecialServers } from "./Server/data/SpecialServers";
import { deleteStockMarket, initStockMarket } from "./StockMarket/StockMarket";
import { canAccessStockMarket, deleteStockMarket, initStockMarket } from "./StockMarket/StockMarket";
import { Terminal } from "./Terminal";
import { dialogBoxCreate } from "./ui/React/DialogBox";
@@ -161,7 +161,7 @@ export function prestigeAugmentation(): void {
}
// Reset Stock market
if (Player.hasWseAccount) {
if (canAccessStockMarket()) {
initStockMarket();
}
@@ -291,7 +291,7 @@ export function prestigeSourceFile(isFlume: boolean): void {
if (Player.bitNodeN === 8) {
Player.money = BitNode8StartingMoney;
}
if (Player.bitNodeN === 8 || Player.activeSourceFileLvl(8) > 0) {
if (canAccessBitNodeFeature(8)) {
Player.hasWseAccount = true;
Player.hasTixApiAccess = true;
}
@@ -314,7 +314,7 @@ export function prestigeSourceFile(isFlume: boolean): void {
}
// Reset Stock market, gang, and corporation
if (Player.hasWseAccount) {
if (canAccessStockMarket()) {
initStockMarket();
} else {
deleteStockMarket();

View File

@@ -1635,28 +1635,44 @@ export interface TIX {
getForecast(sym: string): number;
/**
* Purchase 4S Market Data Access.
* Purchase 4S Market Data UI access (UI only).
*
* You need to have a WSE account. Note that this feature only unlocks access to 4S Market Data in the Stock Market
* UI. If you want to access 4S Market Data via NS APIs, you have to unlock "4S Market Data TIX API access" via
* {@link TIX.purchase4SMarketDataTixApi | purchase4SMarketDataTixApi}, which is unrelated to this feature.
*
* @remarks RAM cost: 2.5 GB
* @returns True if you successfully purchased it or if you already have access, false otherwise.
*/
purchase4SMarketData(): boolean;
/**
* Purchase 4S Market Data TIX API Access.
* Purchase 4S Market Data TIX API access (NS APIs only).
*
* You need to have TIX API access. Note that this feature only unlocks access to 4S Market Data via NS APIs.
*
* @remarks RAM cost: 2.5 GB
* @returns True if you successfully purchased it or if you already have access, false otherwise.
*/
purchase4SMarketDataTixApi(): boolean;
/**
* Purchase WSE Account.
* Purchase a WSE account.
*
* You need to have this account to perform actions via the Stock Market UI. Note that if you want to perform actions
* via NS APIs, you need to have TIX API access, not this account.
*
* @remarks RAM cost: 2.5 GB
* @returns True if you successfully purchased it or if you already have access, false otherwise.
*/
purchaseWseAccount(): boolean;
/**
* Purchase TIX API Access
* Purchase TIX API access.
*
* You need to have TIX API access to perform actions via NS APIs. Note that you can buy TIX API access without a WSE
* account.
*
* @remarks RAM cost: 2.5 GB
* @returns True if you successfully purchased it or if you already have access, false otherwise.
*/

View File

@@ -150,7 +150,7 @@ export function loadStockMarket(saveString: string): void {
console.error(error);
console.error("Invalid StockMarketSave:", saveString);
deleteStockMarket();
if (Player.hasWseAccount) {
if (canAccessStockMarket()) {
initStockMarket();
}
const errorMessage = `Cannot load data of StockMarket. StockMarket is reset.`;
@@ -163,6 +163,18 @@ export function loadStockMarket(saveString: string): void {
StockMarket = stockMarketData as IStockMarket;
}
export function canAccessStockMarket(): boolean {
return Player.hasWseAccount || Player.hasTixApiAccess;
}
export function isStockMarketInitialized(): boolean {
return StockMarket.lastUpdate > 0;
}
/**
* After calling this function, the stock market will be back to the uninitialized state (i.e.,
* isStockMarketInitialized() returns false).
*/
export function deleteStockMarket(): void {
StockMarket = getDefaultEmptyStockMarket();
}
@@ -187,7 +199,7 @@ export function initStockMarket(): void {
StockMarket.Orders = orders;
StockMarket.storedCycles = 0;
StockMarket.lastUpdate = 0;
StockMarket.lastUpdate = Date.now();
StockMarket.ticksUntilCycle = getRandomIntInclusive(1, StockMarketConstants.TicksPerCycle);
initSymbolToStockMap();
}

View File

@@ -10,7 +10,7 @@ import { getStockMarket4SDataCost, getStockMarket4STixApiCost } from "../StockMa
import { StockMarketConstants } from "../data/Constants";
import { Player } from "@player";
import { Money } from "../../ui/React/Money";
import { initStockMarket } from "../StockMarket";
import { initStockMarket, isStockMarketInitialized } from "../StockMarket";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
@@ -47,7 +47,7 @@ function Purchase4SMarketDataTixApiAccessButton(props: IProps): React.ReactEleme
if (Player.has4SDataTixApi) {
return (
<Typography>
Market Data TIX API Access <CheckIcon />
4S Market Data TIX API Access <CheckIcon />
</Typography>
);
}
@@ -91,19 +91,21 @@ function PurchaseWseAccountButton(props: IProps): React.ReactElement {
return;
}
Player.hasWseAccount = true;
initStockMarket();
if (!isStockMarketInitialized()) {
initStockMarket();
}
Player.loseMoney(StockMarketConstants.WseAccountCost, "stock");
props.rerender();
}
const cost = StockMarketConstants.WseAccountCost;
let tooltipTitle = "Let you trade stock";
let tooltipTitle = "Let you trade stock via UI";
if (!Player.canAfford(cost)) {
tooltipTitle = "You do not have enough money";
}
return (
<>
<Typography>To begin trading, you must first purchase an account:</Typography>
<Typography>If you want to trade via Stock Market dashboard (UI), you must purchase a WSE account.</Typography>
<Tooltip title={<Typography>{tooltipTitle}</Typography>}>
<span>
<Button disabled={!Player.canAfford(cost)} onClick={purchaseWseAccount}>
@@ -121,13 +123,13 @@ function PurchaseTixApiAccessButton(props: IProps): React.ReactElement {
if (Player.hasTixApiAccess) {
return;
}
if (!Player.hasWseAccount) {
return;
}
if (!Player.canAfford(StockMarketConstants.TixApiCost)) {
return;
}
Player.hasTixApiAccess = true;
if (!isStockMarketInitialized()) {
initStockMarket();
}
Player.loseMoney(StockMarketConstants.TixApiCost, "stock");
props.rerender();
}
@@ -140,21 +142,26 @@ function PurchaseTixApiAccessButton(props: IProps): React.ReactElement {
);
}
const cost = StockMarketConstants.TixApiCost;
let tooltipTitle = "Let you trade stock through Netscript";
if (!Player.hasWseAccount) {
tooltipTitle = "Requires WSE Account";
} else if (!Player.canAfford(cost)) {
let tooltipTitle = "Let you trade stock via NS APIs";
if (!Player.canAfford(cost)) {
tooltipTitle = "You do not have enough money";
}
return (
<Tooltip title={<Typography>{tooltipTitle}</Typography>}>
<span>
<Button disabled={!Player.hasWseAccount || !Player.canAfford(cost)} onClick={purchaseTixApiAccess}>
Buy Trade Information eXchange (TIX) API Access -&nbsp;
<Money money={cost} forPurchase={true} />
</Button>
</span>
</Tooltip>
<>
<Typography>
TIX, short for Trade Information eXchange, is the communications protocol used by the WSE. Purchasing access to
the TIX API lets you write code to create your own algorithmic/automated trading strategies.
</Typography>
<Typography>If you want to trade via NS APIs, you must purchase TIX API access.</Typography>
<Tooltip title={<Typography>{tooltipTitle}</Typography>}>
<span>
<Button disabled={!Player.canAfford(cost)} onClick={purchaseTixApiAccess}>
Buy Trade Information eXchange (TIX) API Access -&nbsp;
<Money money={cost} forPurchase={true} />
</Button>
</span>
</Tooltip>
</>
);
}
@@ -179,7 +186,7 @@ function Purchase4SMarketDataButton(props: IProps): React.ReactElement {
if (Player.has4SData) {
return (
<Typography>
4S Market Data Access <CheckIcon />
4S Market Data UI Access <CheckIcon />
</Typography>
);
}
@@ -213,16 +220,16 @@ export function InfoAndPurchases(props: IProps): React.ReactElement {
<>
<Typography variant="h4">Welcome to the World Stock Exchange (WSE)!</Typography>
<Typography variant="h5" color="primary">
WSE Account
</Typography>
<PurchaseWseAccountButton {...props} />
<Typography variant="h5" color="primary">
Trade Information eXchange (TIX) API
</Typography>
<Typography>
TIX, short for Trade Information eXchange, is the communications protocol used by the WSE. Purchasing access to
the TIX API lets you write code to create your own algorithmic/automated trading strategies.
</Typography>
<PurchaseTixApiAccessButton {...props} />
<Typography variant="h5" color="primary">
{FactionName.FourSigma} (4S) Market Data Feed
</Typography>
@@ -235,6 +242,7 @@ export function InfoAndPurchases(props: IProps): React.ReactElement {
</Typography>
<Purchase4SMarketDataTixApiAccessButton {...props} />
<Purchase4SMarketDataButton {...props} />
<Typography>
Commission Fees: Every transaction you make has a{" "}
<Money money={StockMarketConstants.StockMarketCommission} forPurchase={true} /> commission fee.

View File

@@ -25,7 +25,7 @@ import { saveObject, loadGame } from "./SaveObject";
import { GetAllServers, initForeignServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings";
import { FormatsNeedToChange } from "./ui/formatNumber";
import { initSymbolToStockMap, processStockPrices } from "./StockMarket/StockMarket";
import { canAccessStockMarket, initSymbolToStockMap, processStockPrices } from "./StockMarket/StockMarket";
import { Terminal } from "./Terminal";
import { Money } from "./ui/React/Money";
@@ -92,7 +92,7 @@ const Engine = {
Player.processWork(numCycles);
// Update stock prices
if (Player.hasWseAccount) {
if (canAccessStockMarket()) {
processStockPrices(numCycles);
}
@@ -236,7 +236,7 @@ const Engine = {
if (await loadGame(saveData)) {
FormatsNeedToChange.emit();
initBitNodeMultipliers();
if (Player.hasWseAccount) {
if (canAccessStockMarket()) {
initSymbolToStockMap();
}
@@ -307,7 +307,7 @@ const Engine = {
processPassiveFactionRepGain(numCyclesOffline);
// Stock Market offline progress
if (Player.hasWseAccount) {
if (canAccessStockMarket()) {
processStockPrices(numCyclesOffline);
}

View File

@@ -384,5 +384,12 @@ export const breakingChanges300: VersionBreakingChange = {
`- "White Ferrari" was renamed to "${convertV2GangEquipmentNames("White Ferrari")}".\n`,
showWarning: false,
},
{
brokenAPIs: [{ name: "purchase4SMarketData" }],
info:
"You have to purchase a WSE account before purchasing 4S Market Data UI access via ns.stock.purchase4SMarketData().\n" +
"Note that this change does not affect 4S Market Data TIX API access (ns.stock.purchase4SMarketDataTixApi()).",
showWarning: false,
},
],
};