Merge pull request #1693 from danielyxie/revert-1692-dev

Revert "bunch of bugfixes"
This commit is contained in:
hydroflame 2021-11-11 20:06:14 -05:00 committed by GitHub
commit 5e0ab2c93a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 316 additions and 345 deletions

42
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -30,6 +30,7 @@
"better-react-mathjax": "^1.0.3",
"clsx": "^1.1.1",
"date-fns": "^2.25.0",
"decimal.js": "7.2.3",
"escodegen": "^1.11.0",
"file-saver": "^1.3.8",
"jquery": "^3.5.0",

@ -27,12 +27,12 @@ export function NewIndustry(corporation: ICorporation, industry: string, name: s
if (cost === undefined) {
throw new Error(`Invalid industry: '${industry}'`);
}
if (corporation.funds < cost) {
if (corporation.funds.lt(cost)) {
throw new Error("Not enough money to create a new division in this industry");
} else if (name === "") {
throw new Error("New division must have a name!");
} else {
corporation.funds = corporation.funds - cost;
corporation.funds = corporation.funds.minus(cost);
corporation.divisions.push(
new Industry({
corp: corporation,
@ -44,10 +44,10 @@ export function NewIndustry(corporation: ICorporation, industry: string, name: s
}
export function NewCity(corporation: ICorporation, division: IIndustry, city: string): void {
if (corporation.funds < CorporationConstants.OfficeInitialCost) {
if (corporation.funds.lt(CorporationConstants.OfficeInitialCost)) {
throw new Error("You don't have enough company funds to open a new office!");
} else {
corporation.funds = corporation.funds - CorporationConstants.OfficeInitialCost;
corporation.funds = corporation.funds.minus(CorporationConstants.OfficeInitialCost);
division.offices[city] = new OfficeSpace({
loc: city,
size: CorporationConstants.OfficeInitialSize,
@ -56,7 +56,7 @@ export function NewCity(corporation: ICorporation, division: IIndustry, city: st
}
export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnlockUpgrade): void {
if (corporation.funds < upgrade[1]) {
if (corporation.funds.lt(upgrade[1])) {
throw new Error("Insufficient funds");
}
corporation.unlock(upgrade);
@ -67,7 +67,7 @@ export function LevelUpgrade(corporation: ICorporation, upgrade: CorporationUpgr
const priceMult = upgrade[2];
const level = corporation.upgrades[upgrade[0]];
const cost = baseCost * Math.pow(priceMult, level);
if (corporation.funds < cost) {
if (corporation.funds.lt(cost)) {
throw new Error("Insufficient funds");
} else {
corporation.upgrade(upgrade);
@ -259,15 +259,15 @@ export function UpgradeOfficeSize(corp: ICorporation, office: OfficeSpace, size:
mult += Math.pow(costMultiplier, initialPriceMult + i);
}
const cost = CorporationConstants.OfficeInitialCost * mult;
if (corp.funds < cost) return;
if (corp.funds.lt(cost)) return;
office.size += size;
corp.funds = corp.funds - cost;
corp.funds = corp.funds.minus(cost);
}
export function ThrowParty(corp: ICorporation, office: OfficeSpace, costPerEmployee: number): number {
const totalCost = costPerEmployee * office.employees.length;
if (corp.funds < totalCost) return 0;
corp.funds = corp.funds - totalCost;
if (corp.funds.lt(totalCost)) return 0;
corp.funds = corp.funds.minus(totalCost);
let mult = 0;
for (let i = 0; i < office.employees.length; ++i) {
mult = office.employees[i].throwParty(costPerEmployee);
@ -277,7 +277,7 @@ export function ThrowParty(corp: ICorporation, office: OfficeSpace, costPerEmplo
}
export function PurchaseWarehouse(corp: ICorporation, division: IIndustry, city: string): void {
if (corp.funds < CorporationConstants.WarehouseInitialCost) return;
if (corp.funds.lt(CorporationConstants.WarehouseInitialCost)) return;
if (division.warehouses[city] instanceof Warehouse) return;
division.warehouses[city] = new Warehouse({
corp: corp,
@ -285,21 +285,21 @@ export function PurchaseWarehouse(corp: ICorporation, division: IIndustry, city:
loc: city,
size: CorporationConstants.WarehouseInitialSize,
});
corp.funds = corp.funds - CorporationConstants.WarehouseInitialCost;
corp.funds = corp.funds.minus(CorporationConstants.WarehouseInitialCost);
}
export function UpgradeWarehouse(corp: ICorporation, division: IIndustry, warehouse: Warehouse): void {
const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1);
++warehouse.level;
warehouse.updateSize(corp, division);
corp.funds = corp.funds - sizeUpgradeCost;
corp.funds = corp.funds.minus(sizeUpgradeCost);
}
export function BuyCoffee(corp: ICorporation, division: IIndustry, office: OfficeSpace): void {
const upgrade = IndustryUpgrades[0];
const cost = office.employees.length * upgrade[1];
if (corp.funds < cost) return;
corp.funds = corp.funds - cost;
if (corp.funds.lt(cost)) return;
corp.funds = corp.funds.minus(cost);
division.upgrade(upgrade, {
corporation: corp,
office: office,
@ -309,8 +309,8 @@ export function BuyCoffee(corp: ICorporation, division: IIndustry, office: Offic
export function HireAdVert(corp: ICorporation, division: IIndustry, office: OfficeSpace): void {
const upgrade = IndustryUpgrades[1];
const cost = upgrade[1] * Math.pow(upgrade[2], division.upgrades[1]);
if (corp.funds < cost) return;
corp.funds = corp.funds - cost;
if (corp.funds.lt(cost)) return;
corp.funds = corp.funds.minus(cost);
division.upgrade(upgrade, {
corporation: corp,
office: office,
@ -340,7 +340,7 @@ export function MakeProduct(
if (isNaN(marketingInvest)) {
throw new Error("Invalid value for marketing investment");
}
if (corp.funds < designInvest + marketingInvest) {
if (corp.funds.lt(designInvest + marketingInvest)) {
throw new Error("You don't have enough company funds to make this large of an investment");
}
const product = new Product({
@ -352,7 +352,7 @@ export function MakeProduct(
if (division.products[product.name] instanceof Product) {
throw new Error(`You already have a product with this name!`);
}
corp.funds = corp.funds - (designInvest + marketingInvest);
corp.funds = corp.funds.minus(designInvest + marketingInvest);
division.products[product.name] = product;
}

@ -14,6 +14,10 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
import { isString } from "../utils/helpers/isString";
// UI Related Imports
import Decimal from "decimal.js";
interface IParams {
name?: string;
}
@ -25,9 +29,9 @@ export class Corporation {
divisions: Industry[] = [];
//Financial stats
funds = 150e9;
revenue = 0;
expenses = 0;
funds = new Decimal(150e9);
revenue = new Decimal(0);
expenses = new Decimal(0);
fundingRound = 0;
public = false; //Publicly traded
totalShares = CorporationConstants.INITIALSHARES; // Total existing shares
@ -61,7 +65,7 @@ export class Corporation {
console.error("Trying to add invalid amount of funds. Report to a developper.");
return;
}
this.funds = this.funds + amt;
this.funds = this.funds.plus(amt);
}
getState(): string {
@ -93,8 +97,8 @@ export class Corporation {
//At the start of a new cycle, calculate profits from previous cycle
if (state === "START") {
this.revenue = 0;
this.expenses = 0;
this.revenue = new Decimal(0);
this.expenses = new Decimal(0);
this.divisions.forEach((ind) => {
if (ind.lastCycleRevenue === -Infinity || ind.lastCycleRevenue === Infinity) {
return;
@ -102,18 +106,18 @@ export class Corporation {
if (ind.lastCycleExpenses === -Infinity || ind.lastCycleExpenses === Infinity) {
return;
}
this.revenue = this.revenue + ind.lastCycleRevenue;
this.expenses = this.expenses + ind.lastCycleExpenses;
this.revenue = this.revenue.plus(ind.lastCycleRevenue);
this.expenses = this.expenses.plus(ind.lastCycleExpenses);
});
const profit = this.revenue - this.expenses;
const cycleProfit = profit * (marketCycles * CorporationConstants.SecsPerMarketCycle);
const profit = this.revenue.minus(this.expenses);
const cycleProfit = profit.times(marketCycles * CorporationConstants.SecsPerMarketCycle);
if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) {
dialogBoxCreate(
"There was an error calculating your Corporations funds and they got reset to 0. " +
"This is a bug. Please report to game developer.<br><br>" +
"(Your funds have been set to $150b for the inconvenience)",
);
this.funds = 150e9;
this.funds = new Decimal(150e9);
}
// Process dividends
@ -143,8 +147,8 @@ export class Corporation {
}
getDividends(): number {
const profit = this.revenue - this.expenses;
const cycleProfit = profit * CorporationConstants.SecsPerMarketCycle;
const profit = this.revenue.minus(this.expenses);
const cycleProfit = profit.times(CorporationConstants.SecsPerMarketCycle);
const totalDividends = (this.dividendPercentage / 100) * cycleProfit;
const dividendsPerShare = totalDividends / this.totalShares;
const dividends = this.numShares * dividendsPerShare * (1 - this.dividendTaxPercentage / 100);
@ -153,18 +157,18 @@ export class Corporation {
determineValuation(): number {
let val,
profit = this.revenue - this.expenses;
profit = this.revenue.minus(this.expenses).toNumber();
if (this.public) {
// Account for dividends
if (this.dividendPercentage > 0) {
profit *= (100 - this.dividendPercentage) / 100;
}
val = this.funds + profit * 85e3;
val = this.funds.toNumber() + profit * 85e3;
val *= Math.pow(1.1, this.divisions.length);
val = Math.max(val, 0);
} else {
val = 10e9 + Math.max(this.funds, 0) / 3; //Base valuation
val = 10e9 + Math.max(this.funds.toNumber(), 0) / 3; //Base valuation
if (profit > 0) {
val += profit * 315e3;
val *= Math.pow(1.1, this.divisions.length);
@ -258,12 +262,12 @@ export class Corporation {
while (this.unlockUpgrades.length <= upgN) {
this.unlockUpgrades.push(0);
}
if (this.funds < price) {
if (this.funds.lt(price)) {
dialogBoxCreate("You don't have enough funds to unlock this!");
return;
}
this.unlockUpgrades[upgN] = 1;
this.funds = this.funds - price;
this.funds = this.funds.minus(price);
// Apply effects for one-time upgrades
if (upgN === 5) {
@ -286,12 +290,12 @@ export class Corporation {
this.upgradeMultipliers.push(1);
}
const totalCost = basePrice * Math.pow(priceMult, this.upgrades[upgN]);
if (this.funds < totalCost) {
if (this.funds.lt(totalCost)) {
dialogBoxCreate("You don't have enough funds to purchase this!");
return;
}
++this.upgrades[upgN];
this.funds = this.funds - totalCost;
this.funds = this.funds.minus(totalCost);
//Increase upgrade multiplier
this.upgradeMultipliers[upgN] = 1 + this.upgrades[upgN] * upgradeAmt;

@ -9,9 +9,9 @@ export interface ICorporation {
divisions: Industry[];
funds: number;
revenue: number;
expenses: number;
funds: any;
revenue: any;
expenses: any;
fundingRound: number;
public: boolean;
totalShares: number;

@ -31,10 +31,10 @@ export interface IIndustry {
prodMult: number;
// Decimal
lastCycleRevenue: number;
lastCycleExpenses: number;
thisCycleRevenue: number;
thisCycleExpenses: number;
lastCycleRevenue: any;
lastCycleExpenses: any;
thisCycleRevenue: any;
thisCycleExpenses: any;
upgrades: number[];

@ -1,5 +1,6 @@
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
import { CityName } from "../Locations/data/CityNames";
import Decimal from "decimal.js";
import { Industries, IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
import { CorporationConstants } from "./data/Constants";
import { EmployeePositions } from "./EmployeePositions";
@ -54,10 +55,10 @@ export class Industry implements IIndustry {
prodMult = 0; //Production multiplier
//Financials
lastCycleRevenue: number;
lastCycleExpenses: number;
thisCycleRevenue: number;
thisCycleExpenses: number;
lastCycleRevenue: any;
lastCycleExpenses: any;
thisCycleRevenue: any;
thisCycleExpenses: any;
//Upgrades
upgrades: number[] = Array(Object.keys(IndustryUpgrades).length).fill(0);
@ -86,10 +87,10 @@ export class Industry implements IIndustry {
this.type = params.type ? params.type : Industries.Agriculture;
//Financials
this.lastCycleRevenue = 0;
this.lastCycleExpenses = 0;
this.thisCycleRevenue = 0;
this.thisCycleExpenses = 0;
this.lastCycleRevenue = new Decimal(0);
this.lastCycleExpenses = new Decimal(0);
this.thisCycleRevenue = new Decimal(0);
this.thisCycleExpenses = new Decimal(0);
this.warehouses = {
[CityName.Aevum]: 0,
@ -398,17 +399,17 @@ export class Industry implements IIndustry {
dialogBoxCreate(
"Something went wrong when compting Corporation's revenue/expenses. This is a bug. Please report to game developer",
);
this.thisCycleRevenue = 0;
this.thisCycleExpenses = 0;
this.thisCycleRevenue = new Decimal(0);
this.thisCycleExpenses = new Decimal(0);
}
this.lastCycleRevenue = this.thisCycleRevenue / (marketCycles * CorporationConstants.SecsPerMarketCycle);
this.lastCycleExpenses = this.thisCycleExpenses / (marketCycles * CorporationConstants.SecsPerMarketCycle);
this.thisCycleRevenue = 0;
this.thisCycleExpenses = 0;
this.lastCycleRevenue = this.thisCycleRevenue.dividedBy(marketCycles * CorporationConstants.SecsPerMarketCycle);
this.lastCycleExpenses = this.thisCycleExpenses.dividedBy(marketCycles * CorporationConstants.SecsPerMarketCycle);
this.thisCycleRevenue = new Decimal(0);
this.thisCycleExpenses = new Decimal(0);
// Once you start making revenue, the player should no longer be
// considered new, and therefore no longer needs the 'tutorial' UI elements
if (this.lastCycleRevenue > 0) {
if (this.lastCycleRevenue.gt(0)) {
this.newInd = false;
}
@ -421,7 +422,7 @@ export class Industry implements IIndustry {
employeeSalary += office.process(marketCycles, corporation, this);
}
}
this.thisCycleExpenses = this.thisCycleExpenses + employeeSalary;
this.thisCycleExpenses = this.thisCycleExpenses.plus(employeeSalary);
// Process change in demand/competition of materials/products
this.processMaterialMarket();
@ -445,15 +446,15 @@ export class Industry implements IIndustry {
// Process production, purchase, and import/export of materials
let res = this.processMaterials(marketCycles, corporation);
if (Array.isArray(res)) {
this.thisCycleRevenue = this.thisCycleRevenue + res[0];
this.thisCycleExpenses = this.thisCycleExpenses + res[1];
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
}
// Process creation, production & sale of products
res = this.processProducts(marketCycles, corporation);
if (Array.isArray(res)) {
this.thisCycleRevenue = this.thisCycleRevenue + res[0];
this.thisCycleExpenses = this.thisCycleExpenses + res[1];
this.thisCycleRevenue = this.thisCycleRevenue.plus(res[0]);
this.thisCycleExpenses = this.thisCycleExpenses.plus(res[1]);
}
}
@ -1475,10 +1476,10 @@ export class Industry implements IIndustry {
division.prodMult = this.prodMult;
division.state = this.state;
division.newInd = this.newInd;
division.lastCycleRevenue = this.lastCycleRevenue + 0;
division.lastCycleExpenses = this.lastCycleExpenses + 0;
division.thisCycleRevenue = this.thisCycleRevenue + 0;
division.thisCycleExpenses = this.thisCycleExpenses + 0;
division.lastCycleRevenue = this.lastCycleRevenue.plus(0);
division.lastCycleExpenses = this.lastCycleExpenses.plus(0);
division.thisCycleRevenue = this.thisCycleRevenue.plus(0);
division.thisCycleExpenses = this.thisCycleExpenses.plus(0);
division.upgrades = this.upgrades.slice();
division.prodMats = this.prodMats.slice();
return division;

@ -38,7 +38,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
isNaN(stock) ||
money < 0 ||
stock < 0 ||
corp.funds < money ||
corp.funds.lt(money) ||
stock > corp.numShares;
function onMoneyChange(event: React.ChangeEvent<HTMLInputElement>): void {
@ -61,7 +61,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
if (money === 0 && stock === 0) return "";
if (isNaN(money) || isNaN(stock) || money < 0 || stock < 0) {
return "ERROR: Invalid value(s) entered";
} else if (corp.funds < money) {
} else if (corp.funds.lt(money)) {
return "ERROR: You do not have this much money to bribe with";
} else if (stock > corp.numShares) {
return "ERROR: You do not have this many shares to bribe with";
@ -84,7 +84,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
"You gained " + numeralWrapper.formatReputation(rep) + " reputation with " + fac.name + " by bribing them.",
);
fac.playerReputation += rep;
corp.funds = corp.funds - money;
corp.funds = corp.funds.minus(money);
corp.numShares -= stock;
props.onClose();
}

@ -32,7 +32,7 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
if (cost === undefined) {
throw new Error(`Invalid industry: '${industry}'`);
}
const disabled = corp.funds < cost || name === "";
const disabled = corp.funds.lt(cost) || name === "";
function newIndustry(): void {
if (disabled) return;

@ -19,7 +19,7 @@ export function ExpandNewCity(props: IProps): React.ReactElement {
const possibleCities = Object.keys(division.offices).filter((cityName: string) => division.offices[cityName] === 0);
const [city, setCity] = useState(possibleCities[0]);
const disabled = corp.funds < CorporationConstants.OfficeInitialCost;
const disabled = corp.funds.lt(CorporationConstants.OfficeInitialCost);
function onCityChange(event: SelectChangeEvent<string>): void {
setCity(event.target.value);

@ -440,7 +440,7 @@ export function IndustryOffice(props: IProps): React.ReactElement {
<br />
<Tooltip title={<Typography>Upgrade the office's size so that it can hold more employees!</Typography>}>
<span>
<Button disabled={corp.funds < 0} onClick={() => setUpgradeOfficeSizeOpen(true)}>
<Button disabled={corp.funds.lt(0)} onClick={() => setUpgradeOfficeSizeOpen(true)}>
Upgrade size
</Button>
</span>
@ -458,7 +458,7 @@ export function IndustryOffice(props: IProps): React.ReactElement {
title={<Typography>Throw an office party to increase your employee's morale and happiness</Typography>}
>
<span>
<Button disabled={corp.funds < 0} onClick={() => setThrowPartyOpen(true)}>
<Button disabled={corp.funds.lt(0)} onClick={() => setThrowPartyOpen(true)}>
Throw Party
</Button>
</span>

@ -79,7 +79,11 @@ function MakeProductButton(): React.ReactElement {
)
}
>
<Button color={shouldFlash() ? "error" : "primary"} onClick={() => setMakeOpen(true)} disabled={corp.funds < 0}>
<Button
color={shouldFlash() ? "error" : "primary"}
onClick={() => setMakeOpen(true)}
disabled={corp.funds.lt(0)}
>
{createProductButtonText}
</Button>
</Tooltip>
@ -93,7 +97,7 @@ function Text(): React.ReactElement {
const [helpOpen, setHelpOpen] = useState(false);
const [researchOpen, setResearchOpen] = useState(false);
const vechain = corp.unlockUpgrades[4] === 1;
const profit = division.lastCycleRevenue - division.lastCycleExpenses;
const profit = division.lastCycleRevenue.minus(division.lastCycleExpenses).toNumber();
let advertisingInfo = false;
const advertisingFactors = division.getAdvertisingFactors();
@ -115,7 +119,7 @@ function Text(): React.ReactElement {
return (
<>
<Typography>
Industry: {division.type} (Corp Funds: <Money money={corp.funds} />)
Industry: {division.type} (Corp Funds: <Money money={corp.funds.toNumber()} />)
</Typography>
<br />
<StatsTable
@ -145,8 +149,8 @@ function Text(): React.ReactElement {
<br />
<StatsTable
rows={[
["Revenue:", <MoneyRate money={division.lastCycleRevenue} />],
["Expenses:", <MoneyRate money={division.lastCycleExpenses} />],
["Revenue:", <MoneyRate money={division.lastCycleRevenue.toNumber()} />],
["Expenses:", <MoneyRate money={division.lastCycleExpenses.toNumber()} />],
["Profit:", <MoneyRate money={profit} />],
]}
/>
@ -237,8 +241,8 @@ function Upgrades(props: { office: OfficeSpace; rerender: () => void }): React.R
}
function onClick(): void {
if (corp.funds < cost) return;
corp.funds = corp.funds - cost;
if (corp.funds.lt(cost)) return;
corp.funds = corp.funds.minus(cost);
division.upgrade(upgrade, {
corporation: corp,
office: props.office,
@ -249,7 +253,7 @@ function Upgrades(props: { office: OfficeSpace; rerender: () => void }): React.R
upgrades.push(
<Tooltip key={index} title={upgrade[5]}>
<span>
<Button disabled={corp.funds < cost} onClick={onClick}>
<Button disabled={corp.funds.lt(cost)} onClick={onClick}>
{upgrade[4]} -&nbsp;
<MoneyCost money={cost} corp={corp} />
</Button>

@ -45,14 +45,14 @@ function WarehouseRoot(props: IProps): React.ReactElement {
// Upgrade Warehouse size button
const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, props.warehouse.level + 1);
const canAffordUpgrade = corp.funds > sizeUpgradeCost;
const canAffordUpgrade = corp.funds.gt(sizeUpgradeCost);
function upgradeWarehouseOnClick(): void {
if (division === null) return;
if (props.warehouse === 0) return;
if (!canAffordUpgrade) return;
++props.warehouse.level;
props.warehouse.updateSize(corp, division);
corp.funds = corp.funds - sizeUpgradeCost;
corp.funds = corp.funds.minus(sizeUpgradeCost);
props.rerender();
}
@ -197,7 +197,7 @@ interface IEmptyProps {
function EmptyWarehouse(props: IEmptyProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const disabled = corp.funds < CorporationConstants.WarehouseInitialCost;
const disabled = corp.funds.lt(CorporationConstants.WarehouseInitialCost);
function purchaseWarehouse(): void {
if (disabled) return;
PurchaseWarehouse(corp, division, props.city);

@ -81,7 +81,7 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
privateShares = Math.round(privateShares / 1e6) * 1e6;
corp.issuedShares += newShares - privateShares;
corp.funds = corp.funds + profit;
corp.funds = corp.funds.plus(profit);
corp.immediatelyUpdateSharePrice();
props.onClose();
dialogBoxCreate(

@ -28,7 +28,7 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
const tooltip = data[5];
function onClick(): void {
if (corp.funds < cost) return;
if (corp.funds.lt(cost)) return;
try {
LevelUpgrade(corp, props.upgrade);
} catch (err) {
@ -40,13 +40,11 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
return (
<Grid item xs={4}>
<Box display="flex" alignItems="center" flexDirection="row-reverse">
<Button disabled={corp.funds < cost} sx={{ mx: 1 }} onClick={onClick}>
<Button disabled={corp.funds.lt(cost)} sx={{ mx: 1 }} onClick={onClick}>
<MoneyCost money={cost} corp={corp} />
</Button>
<Tooltip title={tooltip}>
<Typography>
{data[4]} - lvl {level}
</Typography>
<Typography>{data[4]} - lvl {level}</Typography>
</Tooltip>
</Box>
</Grid>

@ -23,7 +23,7 @@ interface IProps {
export function MoneyCost(props: IProps): React.ReactElement {
const classes = useStyles();
if (props.corp.funds <= props.money)
if (!props.corp.funds.gt(props.money))
return <span className={classes.unbuyable}>{numeralWrapper.formatMoney(props.money)}</span>;
return <span className={classes.money}>{numeralWrapper.formatMoney(props.money)}</span>;

@ -36,7 +36,7 @@ interface IProps {
export function Overview({ rerender }: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const profit: number = corp.revenue - corp.expenses;
const profit: number = corp.revenue.minus(corp.expenses).toNumber();
const multRows: any[][] = [];
function appendMult(name: string, value: number): void {
@ -57,9 +57,9 @@ export function Overview({ rerender }: IProps): React.ReactElement {
<>
<StatsTable
rows={[
["Total Funds:", <Money money={corp.funds} />],
["Total Revenue:", <MoneyRate money={corp.revenue} />],
["Total Expenses:", <MoneyRate money={corp.expenses} />],
["Total Funds:", <Money money={corp.funds.toNumber()} />],
["Total Revenue:", <MoneyRate money={corp.revenue.toNumber()} />],
["Total Expenses:", <MoneyRate money={corp.expenses.toNumber()} />],
["Publicly Traded:", corp.public ? "Yes" : "No"],
["Owned Stock Shares:", numeralWrapper.format(corp.numShares, "0.000a")],
["Stock Price:", corp.public ? <Money money={corp.sharePrice} /> : "N/A"],

@ -25,24 +25,14 @@ function BulkPurchaseText(props: IBulkPurchaseTextProps): React.ReactElement {
const maxAmount = (props.warehouse.size - props.warehouse.sizeUsed) / matSize;
if (parsedAmt * matSize > maxAmount) {
return (
<>
<Typography color={"error"}>Not enough warehouse space to purchase this amount</Typography>
</>
);
return <><Typography color={"error"}>Not enough warehouse space to purchase this amount</Typography></>;
} else if (isNaN(cost)) {
return (
<>
<Typography color={"error"}>Invalid put for Bulk Purchase amount</Typography>
</>
);
return <><Typography color={"error"}>Invalid put for Bulk Purchase amount</Typography></>;
} else {
return (
<>
<Typography>
<><Typography>
Purchasing {numeralWrapper.format(parsedAmt, "0,0.00")} of {props.mat.name} will cost{" "}
{numeralWrapper.formatMoney(cost)}
</Typography>
{numeralWrapper.formatMoney(cost)}</Typography>
</>
);
}
@ -72,8 +62,8 @@ function BulkPurchase(props: IBPProps): React.ReactElement {
dialogBoxCreate("Invalid input amount");
} else {
const cost = amount * props.mat.bCost;
if (corp.funds > cost) {
corp.funds = corp.funds - cost;
if (corp.funds.gt(cost)) {
corp.funds = corp.funds.minus(cost);
props.mat.qty += amount;
} else {
dialogBoxCreate(`You cannot afford this purchase.`);

@ -23,7 +23,7 @@ export function ThrowPartyModal(props: IProps): React.ReactElement {
const [cost, setCost] = useState(0);
const totalCost = cost * props.office.employees.length;
const canParty = corp.funds >= totalCost;
const canParty = corp.funds.gte(totalCost);
function changeCost(event: React.ChangeEvent<HTMLInputElement>): void {
let x = parseFloat(event.target.value);
if (isNaN(x)) x = 0;

@ -22,7 +22,7 @@ export function UnlockUpgrade(props: IProps): React.ReactElement {
const data = props.upgradeData;
const tooltip = data[3];
function onClick(): void {
if (corp.funds < data[1]) return;
if (corp.funds.lt(data[1])) return;
try {
UU(corp, props.upgradeData);
} catch (err) {
@ -34,7 +34,7 @@ export function UnlockUpgrade(props: IProps): React.ReactElement {
return (
<Grid item xs={4}>
<Box display="flex" alignItems="center" flexDirection="row-reverse">
<Button disabled={corp.funds < data[1]} sx={{ mx: 1 }} onClick={onClick}>
<Button disabled={corp.funds.lt(data[1])} sx={{ mx: 1 }} onClick={onClick}>
<MoneyCost money={data[1]} corp={corp} />
</Button>
<Tooltip title={tooltip}>

@ -23,7 +23,7 @@ interface IUpgradeButton {
function UpgradeSizeButton(props: IUpgradeButton): React.ReactElement {
const corp = useCorporation();
function upgradeSize(cost: number, size: number): void {
if (corp.funds < cost) {
if (corp.funds.lt(cost)) {
return;
}
@ -34,7 +34,7 @@ function UpgradeSizeButton(props: IUpgradeButton): React.ReactElement {
return (
<Tooltip title={numeralWrapper.formatMoney(props.cost)}>
<span>
<Button disabled={corp.funds < props.cost} onClick={() => upgradeSize(props.cost, props.size)}>
<Button disabled={corp.funds.lt(props.cost)} onClick={() => upgradeSize(props.cost, props.size)}>
+{props.size}
</Button>
</span>
@ -63,7 +63,7 @@ export function UpgradeOfficeSizeModal(props: IProps): React.ReactElement {
const upgradeCost15 = CorporationConstants.OfficeInitialCost * mult;
//Calculate max upgrade size and cost
const maxMult = corp.funds / CorporationConstants.OfficeInitialCost;
const maxMult = corp.funds.dividedBy(CorporationConstants.OfficeInitialCost).toNumber();
let maxNum = 1;
mult = Math.pow(costMultiplier, initialPriceMult);
while (maxNum < 50) {

@ -19,13 +19,13 @@ interface IProps {
export function Corporation(props: IProps): React.ReactElement {
function addTonsCorporationFunds(): void {
if (props.player.corporation) {
props.player.corporation.funds = props.player.corporation.funds + 1e99;
props.player.corporation.funds = props.player.corporation.funds.plus(1e99);
}
}
function resetCorporationFunds(): void {
if (props.player.corporation) {
props.player.corporation.funds = props.player.corporation.funds - props.player.corporation.funds;
props.player.corporation.funds = props.player.corporation.funds.minus(props.player.corporation.funds);
}
}

@ -93,7 +93,8 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
const repCost = aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
const hasReq = props.faction.playerReputation >= repCost;
const hasRep = hasAugmentationPrereqs(aug);
const hasCost = aug.baseCost !== 0 && player.money > aug.baseCost * props.faction.getInfo().augmentationPriceMult;
const hasCost =
aug.baseCost !== 0 && player.money.gt(aug.baseCost * props.faction.getInfo().augmentationPriceMult);
return hasCost && hasReq && hasRep;
}
const buy = augs.filter(canBuy).sort((augName1, augName2) => {

@ -37,7 +37,7 @@ export function DonateOption(props: IProps): React.ReactElement {
function canDonate(): boolean {
if (donateAmt === null) return false;
if (isNaN(donateAmt) || donateAmt <= 0) return false;
if (props.p.money < donateAmt) return false;
if (props.p.money.lt(donateAmt)) return false;
return true;
}
@ -66,7 +66,7 @@ export function DonateOption(props: IProps): React.ReactElement {
function Status(): React.ReactElement {
if (donateAmt === null) return <></>;
if (!canDonate()) {
if (props.p.money < donateAmt) return <Typography>Insufficient funds</Typography>;
if (props.p.money.lt(donateAmt)) return <Typography>Insufficient funds</Typography>;
return <Typography>Invalid donate amount entered!</Typography>;
}
return (

@ -88,7 +88,7 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
const repCost = aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
const hasReq = hasAugmentationPrereqs(aug);
const hasRep = props.faction.playerReputation >= repCost;
const hasCost = aug.baseCost === 0 || props.p.money > aug.baseCost * props.faction.getInfo().augmentationPriceMult;
const hasCost = aug.baseCost === 0 || props.p.money.gt(aug.baseCost * props.faction.getInfo().augmentationPriceMult);
// Determine UI properties
const color: "error" | "primary" = !hasReq || !hasRep || !hasCost ? "error" : "primary";

@ -316,7 +316,7 @@ export class GangMember {
// Prevent purchasing of already-owned upgrades
if (this.augmentations.includes(upg.name) || this.upgrades.includes(upg.name)) return false;
if (player.money < gang.getUpgradeCost(upg)) return false;
if (player.money.lt(gang.getUpgradeCost(upg))) return false;
player.loseMoney(gang.getUpgradeCost(upg), "gang");
if (upg.type === "g") {
this.augmentations.push(upg.name);

@ -28,7 +28,7 @@ function NextReveal(props: INextRevealProps): React.ReactElement {
const upgrades = Object.keys(GangMemberUpgrades)
.filter((upgName: string) => {
const upg = GangMemberUpgrades[upgName];
if (player.money > gang.getUpgradeCost(upg)) return false;
if (player.money.gt(gang.getUpgradeCost(upg))) return false;
if (upg.type !== props.type) return false;
if (props.upgrades.includes(upgName)) return false;
return true;
@ -96,7 +96,7 @@ function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
return Object.keys(GangMemberUpgrades)
.filter((upgName: string) => {
const upg = GangMemberUpgrades[upgName];
if (player.money < gang.getUpgradeCost(upg)) return false;
if (player.money.lt(gang.getUpgradeCost(upg))) return false;
if (upg.type !== type) return false;
if (list.includes(upgName)) return false;
return true;

@ -98,14 +98,14 @@ export function getMaxNumberLevelUpgrades(
throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`);
}
if (player.money < nodeObj.calculateLevelUpgradeCost(1, player.hacknet_node_level_cost_mult)) {
if (player.money.lt(nodeObj.calculateLevelUpgradeCost(1, player.hacknet_node_level_cost_mult))) {
return 0;
}
let min = 1;
let max = maxLevel - 1;
const levelsToMax = maxLevel - nodeObj.level;
if (player.money > nodeObj.calculateLevelUpgradeCost(levelsToMax, player.hacknet_node_level_cost_mult)) {
if (player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, player.hacknet_node_level_cost_mult))) {
return levelsToMax;
}
@ -113,13 +113,13 @@ export function getMaxNumberLevelUpgrades(
const curr = ((min + max) / 2) | 0;
if (
curr !== maxLevel &&
player.money > nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult) &&
player.money < nodeObj.calculateLevelUpgradeCost(curr + 1, player.hacknet_node_level_cost_mult)
player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult)) &&
player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, player.hacknet_node_level_cost_mult))
) {
return Math.min(levelsToMax, curr);
} else if (player.money < nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult)) {
} else if (player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult))) {
max = curr - 1;
} else if (player.money > nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult)) {
} else if (player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult))) {
min = curr + 1;
} else {
return Math.min(levelsToMax, curr);
@ -138,7 +138,7 @@ export function getMaxNumberRamUpgrades(
throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`);
}
if (player.money < nodeObj.calculateRamUpgradeCost(1, player.hacknet_node_ram_cost_mult)) {
if (player.money.lt(nodeObj.calculateRamUpgradeCost(1, player.hacknet_node_ram_cost_mult))) {
return 0;
}
@ -148,13 +148,13 @@ export function getMaxNumberRamUpgrades(
} else {
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram));
}
if (player.money > nodeObj.calculateRamUpgradeCost(levelsToMax, player.hacknet_node_ram_cost_mult)) {
if (player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, player.hacknet_node_ram_cost_mult))) {
return levelsToMax;
}
//We'll just loop until we find the max
for (let i = levelsToMax - 1; i >= 0; --i) {
if (player.money > nodeObj.calculateRamUpgradeCost(i, player.hacknet_node_ram_cost_mult)) {
if (player.money.gt(nodeObj.calculateRamUpgradeCost(i, player.hacknet_node_ram_cost_mult))) {
return i;
}
}
@ -171,14 +171,14 @@ export function getMaxNumberCoreUpgrades(
throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`);
}
if (player.money < nodeObj.calculateCoreUpgradeCost(1, player.hacknet_node_core_cost_mult)) {
if (player.money.lt(nodeObj.calculateCoreUpgradeCost(1, player.hacknet_node_core_cost_mult))) {
return 0;
}
let min = 1;
let max = maxLevel - 1;
const levelsToMax = maxLevel - nodeObj.cores;
if (player.money > nodeObj.calculateCoreUpgradeCost(levelsToMax, player.hacknet_node_core_cost_mult)) {
if (player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, player.hacknet_node_core_cost_mult))) {
return levelsToMax;
}
@ -187,13 +187,13 @@ export function getMaxNumberCoreUpgrades(
const curr = ((min + max) / 2) | 0;
if (
curr != maxLevel &&
player.money > nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult) &&
player.money < nodeObj.calculateCoreUpgradeCost(curr + 1, player.hacknet_node_core_cost_mult)
player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult)) &&
player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, player.hacknet_node_core_cost_mult))
) {
return Math.min(levelsToMax, curr);
} else if (player.money < nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult)) {
} else if (player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult))) {
max = curr - 1;
} else if (player.money > nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult)) {
} else if (player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult))) {
min = curr + 1;
} else {
return Math.min(levelsToMax, curr);
@ -480,7 +480,7 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
player.hashManager.refundUpgrade(upgName);
return false;
}
corp.funds = corp.funds + upg.value;
corp.funds = corp.funds.plus(upg.value);
break;
}
case "Reduce Minimum Security": {

@ -155,7 +155,7 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
<Money money={upgradeCacheCost} player={props.player} />
</>
);
if (props.player.money < upgradeCacheCost) {
if (props.player.money.lt(upgradeCacheCost)) {
} else {
}
}

@ -33,7 +33,7 @@ export function PlayerInfo(props: IProps): React.ReactElement {
<>
<Typography>
Money:
<Money money={props.player.money} />
<Money money={props.player.money.toNumber()} />
</Typography>
{hasServers && (

@ -2,11 +2,18 @@ import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer";
export function getHospitalizationCost(p: IPlayer): number {
if (p.money < 0) {
let money;
if (typeof p.money === "number") {
money = p.money;
} else {
money = p.money.toNumber();
}
if (money < 0) {
return 0;
}
return Math.min(p.money * 0.1, (p.max_hp - p.hp) * CONSTANTS.HospitalCostPerHp);
return Math.min(money * 0.1, (p.max_hp - p.hp) * CONSTANTS.HospitalCostPerHp);
}
export function calculateHospitalizationCost(p: IPlayer, damage: number): number {

@ -136,7 +136,7 @@ function iTutorialPrevStep(): void {
function iTutorialEnd(): void {
ITutorial.isRunning = false;
ITutorial.currStep = iTutorialSteps.End;
Player.getHomeComputer().messages.push(LiteratureNames.HackersStartingHandbook);
ITutorialEvents.emit();
}

@ -450,7 +450,7 @@ export function NetscriptSingularity(
case CityName.NewTokyo:
case CityName.Ishima:
case CityName.Volhaven:
if (player.money < CONSTANTS.TravelCost) {
if (player.money.lt(CONSTANTS.TravelCost)) {
throw helper.makeRuntimeErrorMsg("travelToCity", "Not enough money to travel.");
}
player.loseMoney(CONSTANTS.TravelCost, "other");
@ -473,7 +473,7 @@ export function NetscriptSingularity(
return false;
}
if (player.money < CONSTANTS.TorRouterCost) {
if (player.money.lt(CONSTANTS.TorRouterCost)) {
workerScript.log("purchaseTor", "You cannot afford to purchase a Tor router.");
return false;
}
@ -520,7 +520,7 @@ export function NetscriptSingularity(
return false;
}
if (player.money < item.price) {
if (player.money.lt(item.price)) {
workerScript.log(
"purchaseProgram",
`Not enough money to purchase '${item.program}'. Need ${numeralWrapper.formatMoney(item.price)}`,
@ -724,7 +724,7 @@ export function NetscriptSingularity(
}
const cost = player.getUpgradeHomeCoresCost();
if (player.money < cost) {
if (player.money.lt(cost)) {
workerScript.log("upgradeHomeCores", `You don't have enough money. Need ${numeralWrapper.formatMoney(cost)}`);
return false;
}
@ -757,7 +757,7 @@ export function NetscriptSingularity(
}
const cost = player.getUpgradeHomeRamCost();
if (player.money < cost) {
if (player.money.lt(cost)) {
workerScript.log("upgradeHomeRam", `You don't have enough money. Need ${numeralWrapper.formatMoney(cost)}`);
return false;
}
@ -1107,7 +1107,7 @@ export function NetscriptSingularity(
workerScript.log("donateToFaction", `Invalid donation amount: '${amt}'.`);
return false;
}
if (player.money < amt) {
if (player.money.lt(amt)) {
workerScript.log(
"donateToFaction",
`You do not have enough money to donate ${numeralWrapper.formatMoney(amt)} to '${name}'`,

@ -319,7 +319,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return true;
}
if (player.money < getStockMarket4SDataCost()) {
if (player.money.lt(getStockMarket4SDataCost())) {
workerScript.log("purchase4SMarketData", "Not enough money to purchase 4S Market Data.");
return false;
}
@ -338,7 +338,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return true;
}
if (player.money < getStockMarket4STixApiCost()) {
if (player.money.lt(getStockMarket4STixApiCost())) {
workerScript.log("purchase4SMarketDataTixApi", "Not enough money to purchase 4S Market Data TIX API");
return false;
}

@ -56,7 +56,7 @@ export interface IPlayer {
numPeopleKilled: number;
location: LocationName;
max_hp: number;
readonly money: number;
readonly money: any;
moneySourceA: MoneySourceTracker;
moneySourceB: MoneySourceTracker;
playtimeSinceLastAug: number;
@ -134,8 +134,6 @@ export interface IPlayer {
className: string;
currentWorkFactionName: string;
workType: string;
workCostMult: number;
workExpMult: number;
currentWorkFactionDescription: string;
timeWorked: number;
workMoneyGained: number;

@ -35,6 +35,8 @@ import { CityName } from "../../Locations/data/CityNames";
import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../../utils/JSONReviver";
import Decimal from "decimal.js";
export class PlayerObject implements IPlayer {
// Class members
augmentations: IPlayerOwnedAugmentation[];
@ -139,8 +141,6 @@ export class PlayerObject implements IPlayer {
className: string;
currentWorkFactionName: string;
workType: string;
workCostMult: number;
workExpMult: number;
currentWorkFactionDescription: string;
timeWorked: number;
workMoneyGained: number;
@ -336,7 +336,7 @@ export class PlayerObject implements IPlayer {
this.faction_rep_mult = 1;
//Money
this.money = 1000;
this.money = new Decimal(1000);
//Location information
this.city = CityName.Sector12;
@ -379,8 +379,6 @@ export class PlayerObject implements IPlayer {
this.isWorking = false;
this.focus = false;
this.workType = "";
this.workCostMult = 1;
this.workExpMult = 1;
this.currentWorkFactionName = "";
this.currentWorkFactionDescription = "";

@ -36,6 +36,7 @@ import {
import { GetServer, AddToAllServers, createUniqueRandomIp } from "../../Server/AllServers";
import { Server } from "../../Server/Server";
import { safetlyCreateUniqueServer } from "../../Server/ServerHelpers";
import { Settings } from "../../Settings/Settings";
import { SpecialServers } from "../../Server/data/SpecialServers";
import { applySourceFile } from "../../SourceFile/applySourceFile";
import { applyExploit } from "../../Exploits/applyExploits";
@ -46,6 +47,8 @@ import { getHospitalizationCost } from "../../Hospital/Hospital";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { HacknetServer } from "../../Hacknet/HacknetServer";
import Decimal from "decimal.js";
import { numeralWrapper } from "../../ui/numeralFormat";
import { IRouter } from "../../ui/Router";
import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
@ -58,7 +61,6 @@ import { Money } from "../../ui/React/Money";
import React from "react";
import { serverMetadata } from "../../Server/data/servers";
import { SnackbarEvents } from "../../ui/React/Snackbar";
import { calculateClassEarnings } from "../formulas/work";
export function init(this: IPlayer): void {
/* Initialize Player's home computer */
@ -100,7 +102,7 @@ export function prestigeAugmentation(this: PlayerObject): void {
this.agility_exp = 0;
this.charisma_exp = 0;
this.money = 1000;
this.money = new Decimal(1000);
this.city = CityName.Sector12;
this.location = LocationName.TravelAgency;
@ -323,7 +325,7 @@ export function setMoney(this: PlayerObject, money: number): void {
console.error("NaN passed into Player.setMoney()");
return;
}
this.money = money;
this.money = new Decimal(money);
}
export function gainMoney(this: PlayerObject, money: number, source: string): void {
@ -331,7 +333,7 @@ export function gainMoney(this: PlayerObject, money: number, source: string): vo
console.error("NaN passed into Player.gainMoney()");
return;
}
this.money = this.money + money;
this.money = this.money.plus(money);
this.recordMoneySource(money, source);
}
@ -340,8 +342,8 @@ export function loseMoney(this: PlayerObject, money: number, source: string): vo
console.error("NaN passed into Player.loseMoney()");
return;
}
if (this.money == Infinity && money === Infinity) return;
this.money = this.money - money;
if (this.money.eq(Infinity) && money === Infinity) return;
this.money = this.money.minus(money);
this.recordMoneySource(-1 * money, source);
}
@ -350,7 +352,7 @@ export function canAfford(this: IPlayer, cost: number): boolean {
console.error(`NaN passed into Player.canAfford()`);
return false;
}
return this.money >= cost;
return this.money.gte(cost);
}
export function recordMoneySource(this: PlayerObject, amt: number, source: string): void {
@ -1303,30 +1305,77 @@ export function startClass(this: IPlayer, router: IRouter, costMult: number, exp
this.isWorking = true;
this.focus = true;
this.workType = CONSTANTS.WorkTypeStudyClass;
this.workCostMult = costMult;
this.workExpMult = expMult;
this.className = className;
const earnings = calculateClassEarnings(this);
this.workMoneyLossRate = earnings.workMoneyLossRate;
this.workHackExpGainRate = earnings.workHackExpGainRate;
this.workStrExpGainRate = earnings.workStrExpGainRate;
this.workDefExpGainRate = earnings.workDefExpGainRate;
this.workDexExpGainRate = earnings.workDexExpGainRate;
this.workAgiExpGainRate = earnings.workAgiExpGainRate;
this.workChaExpGainRate = earnings.workChaExpGainRate;
const gameCPS = 1000 / CONSTANTS._idleSpeed;
//Find cost and exp gain per game cycle
let cost = 0;
let hackExp = 0,
strExp = 0,
defExp = 0,
dexExp = 0,
agiExp = 0,
chaExp = 0;
const hashManager = this.hashManager;
switch (className) {
case CONSTANTS.ClassStudyComputerScience:
hackExp = ((CONSTANTS.ClassStudyComputerScienceBaseExp * expMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassDataStructures:
cost = (CONSTANTS.ClassDataStructuresBaseCost * costMult) / gameCPS;
hackExp = ((CONSTANTS.ClassDataStructuresBaseExp * expMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassNetworks:
cost = (CONSTANTS.ClassNetworksBaseCost * costMult) / gameCPS;
hackExp = ((CONSTANTS.ClassNetworksBaseExp * expMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassAlgorithms:
cost = (CONSTANTS.ClassAlgorithmsBaseCost * costMult) / gameCPS;
hackExp = ((CONSTANTS.ClassAlgorithmsBaseExp * expMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassManagement:
cost = (CONSTANTS.ClassManagementBaseCost * costMult) / gameCPS;
chaExp = ((CONSTANTS.ClassManagementBaseExp * expMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassLeadership:
cost = (CONSTANTS.ClassLeadershipBaseCost * costMult) / gameCPS;
chaExp = ((CONSTANTS.ClassLeadershipBaseExp * expMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassGymStrength:
cost = (CONSTANTS.ClassGymBaseCost * costMult) / gameCPS;
strExp = (expMult / gameCPS) * hashManager.getTrainingMult();
break;
case CONSTANTS.ClassGymDefense:
cost = (CONSTANTS.ClassGymBaseCost * costMult) / gameCPS;
defExp = (expMult / gameCPS) * hashManager.getTrainingMult();
break;
case CONSTANTS.ClassGymDexterity:
cost = (CONSTANTS.ClassGymBaseCost * costMult) / gameCPS;
dexExp = (expMult / gameCPS) * hashManager.getTrainingMult();
break;
case CONSTANTS.ClassGymAgility:
cost = (CONSTANTS.ClassGymBaseCost * costMult) / gameCPS;
agiExp = (expMult / gameCPS) * hashManager.getTrainingMult();
break;
default:
throw new Error("ERR: Invalid/unrecognized class name");
return;
}
this.workMoneyLossRate = cost;
this.workHackExpGainRate = hackExp * this.hacking_exp_mult * BitNodeMultipliers.ClassGymExpGain;
this.workStrExpGainRate = strExp * this.strength_exp_mult * BitNodeMultipliers.ClassGymExpGain;
this.workDefExpGainRate = defExp * this.defense_exp_mult * BitNodeMultipliers.ClassGymExpGain;
this.workDexExpGainRate = dexExp * this.dexterity_exp_mult * BitNodeMultipliers.ClassGymExpGain;
this.workAgiExpGainRate = agiExp * this.agility_exp_mult * BitNodeMultipliers.ClassGymExpGain;
this.workChaExpGainRate = chaExp * this.charisma_exp_mult * BitNodeMultipliers.ClassGymExpGain;
router.toWork();
}
export function takeClass(this: IPlayer, numCycles: number): boolean {
this.timeWorked += CONSTANTS._idleSpeed * numCycles;
const earnings = calculateClassEarnings(this);
this.workMoneyLossRate = earnings.workMoneyLossRate;
this.workHackExpGainRate = earnings.workHackExpGainRate;
this.workStrExpGainRate = earnings.workStrExpGainRate;
this.workDefExpGainRate = earnings.workDefExpGainRate;
this.workDexExpGainRate = earnings.workDexExpGainRate;
this.workAgiExpGainRate = earnings.workAgiExpGainRate;
this.workChaExpGainRate = earnings.workChaExpGainRate;
this.processWorkEarnings(numCycles);
return false;
}
@ -2021,7 +2070,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!illuminatiFac.isMember &&
!illuminatiFac.alreadyInvited &&
numAugmentations >= 30 &&
this.money >= 150000000000 &&
this.money.gte(150000000000) &&
this.hacking >= 1500 &&
this.strength >= 1200 &&
this.defense >= 1200 &&
@ -2038,7 +2087,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!daedalusFac.isMember &&
!daedalusFac.alreadyInvited &&
numAugmentations >= Math.round(30 * BitNodeMultipliers.DaedalusAugsRequirement) &&
this.money >= 100000000000 &&
this.money.gte(100000000000) &&
(this.hacking >= 2500 ||
(this.strength >= 1500 && this.defense >= 1500 && this.dexterity >= 1500 && this.agility >= 1500))
) {
@ -2052,7 +2101,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!covenantFac.isMember &&
!covenantFac.alreadyInvited &&
numAugmentations >= 20 &&
this.money >= 75000000000 &&
this.money.gte(75000000000) &&
this.hacking >= 850 &&
this.strength >= 850 &&
this.defense >= 850 &&
@ -2231,7 +2280,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!chongqingFac.isBanned &&
!chongqingFac.isMember &&
!chongqingFac.alreadyInvited &&
this.money >= 20000000 &&
this.money.gte(20000000) &&
this.city == CityName.Chongqing
) {
invitedFactions.push(chongqingFac);
@ -2243,7 +2292,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!sector12Fac.isBanned &&
!sector12Fac.isMember &&
!sector12Fac.alreadyInvited &&
this.money >= 15000000 &&
this.money.gte(15000000) &&
this.city == CityName.Sector12
) {
invitedFactions.push(sector12Fac);
@ -2255,7 +2304,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!newtokyoFac.isBanned &&
!newtokyoFac.isMember &&
!newtokyoFac.alreadyInvited &&
this.money >= 20000000 &&
this.money.gte(20000000) &&
this.city == CityName.NewTokyo
) {
invitedFactions.push(newtokyoFac);
@ -2267,7 +2316,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!aevumFac.isBanned &&
!aevumFac.isMember &&
!aevumFac.alreadyInvited &&
this.money >= 40000000 &&
this.money.gte(40000000) &&
this.city == CityName.Aevum
) {
invitedFactions.push(aevumFac);
@ -2279,7 +2328,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!ishimaFac.isBanned &&
!ishimaFac.isMember &&
!ishimaFac.alreadyInvited &&
this.money >= 30000000 &&
this.money.gte(30000000) &&
this.city == CityName.Ishima
) {
invitedFactions.push(ishimaFac);
@ -2291,7 +2340,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!volhavenFac.isBanned &&
!volhavenFac.isMember &&
!volhavenFac.alreadyInvited &&
this.money >= 50000000 &&
this.money.gte(50000000) &&
this.city == CityName.Volhaven
) {
invitedFactions.push(volhavenFac);
@ -2348,7 +2397,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
this.dexterity >= 200 &&
this.agility >= 200 &&
(this.city == CityName.Aevum || this.city == CityName.Sector12) &&
this.money >= 10000000 &&
this.money.gte(10000000) &&
this.karma <= -90 &&
!allCompanies.includes(LocationName.Sector12CIA) &&
!allCompanies.includes(LocationName.Sector12NSA)
@ -2365,7 +2414,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
(allPositions.includes("Chief Technology Officer") ||
allPositions.includes("Chief Financial Officer") ||
allPositions.includes("Chief Executive Officer")) &&
this.money >= 15000000 &&
this.money.gte(15000000) &&
this.karma <= -22
) {
invitedFactions.push(silhouetteFac);
@ -2398,7 +2447,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
this.dexterity >= 30 &&
this.agility >= 30 &&
this.karma <= -9 &&
this.money >= 1000000
this.money.gte(1000000)
) {
invitedFactions.push(slumsnakesFac);
}
@ -2441,7 +2490,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!tiandihuiFac.isBanned &&
!tiandihuiFac.isMember &&
!tiandihuiFac.alreadyInvited &&
this.money >= 1000000 &&
this.money.gte(1000000) &&
this.hacking >= 50 &&
(this.city == CityName.Chongqing || this.city == CityName.NewTokyo || this.city == CityName.Ishima)
) {

@ -64,7 +64,7 @@ export function SleeveAugmentationsModal(props: IProps): React.ReactElement {
return (
<TableRow key={aug.name}>
<TableCell>
<Button onClick={() => purchaseAugmentation(aug)} disabled={player.money < aug.startingCost}>
<Button onClick={() => purchaseAugmentation(aug)} disabled={player.money.lt(aug.startingCost)}>
Buy
</Button>
</TableCell>

@ -174,9 +174,9 @@ export function SleeveElem(props: IProps): React.ReactElement {
<Grid item xs={3}>
<StatsElement sleeve={props.sleeve} />
<Button onClick={() => setStatsOpen(true)}>More Stats</Button>
<Tooltip title={player.money < CONSTANTS.TravelCost ? <Typography>Insufficient funds</Typography> : ""}>
<Tooltip title={player.money.lt(CONSTANTS.TravelCost) ? <Typography>Insufficient funds</Typography> : ""}>
<span>
<Button onClick={() => setTravelOpen(true)} disabled={player.money < CONSTANTS.TravelCost}>
<Button onClick={() => setTravelOpen(true)} disabled={player.money.lt(CONSTANTS.TravelCost)}>
Travel
</Button>
</span>

@ -1,80 +0,0 @@
import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { IPlayer } from "../IPlayer";
export interface WorkEarnings {
workMoneyLossRate: number;
workHackExpGainRate: number;
workStrExpGainRate: number;
workDefExpGainRate: number;
workDexExpGainRate: number;
workAgiExpGainRate: number;
workChaExpGainRate: number;
}
export function calculateClassEarnings(player: IPlayer): WorkEarnings {
const gameCPS = 1000 / CONSTANTS._idleSpeed;
//Find cost and exp gain per game cycle
let cost = 0;
let hackExp = 0,
strExp = 0,
defExp = 0,
dexExp = 0,
agiExp = 0,
chaExp = 0;
const hashManager = player.hashManager;
switch (player.className) {
case CONSTANTS.ClassStudyComputerScience:
hackExp =
((CONSTANTS.ClassStudyComputerScienceBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassDataStructures:
cost = (CONSTANTS.ClassDataStructuresBaseCost * player.workCostMult) / gameCPS;
hackExp = ((CONSTANTS.ClassDataStructuresBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassNetworks:
cost = (CONSTANTS.ClassNetworksBaseCost * player.workCostMult) / gameCPS;
hackExp = ((CONSTANTS.ClassNetworksBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassAlgorithms:
cost = (CONSTANTS.ClassAlgorithmsBaseCost * player.workCostMult) / gameCPS;
hackExp = ((CONSTANTS.ClassAlgorithmsBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassManagement:
cost = (CONSTANTS.ClassManagementBaseCost * player.workCostMult) / gameCPS;
chaExp = ((CONSTANTS.ClassManagementBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassLeadership:
cost = (CONSTANTS.ClassLeadershipBaseCost * player.workCostMult) / gameCPS;
chaExp = ((CONSTANTS.ClassLeadershipBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
break;
case CONSTANTS.ClassGymStrength:
cost = (CONSTANTS.ClassGymBaseCost * player.workCostMult) / gameCPS;
strExp = (player.workExpMult / gameCPS) * hashManager.getTrainingMult();
break;
case CONSTANTS.ClassGymDefense:
cost = (CONSTANTS.ClassGymBaseCost * player.workCostMult) / gameCPS;
defExp = (player.workExpMult / gameCPS) * hashManager.getTrainingMult();
break;
case CONSTANTS.ClassGymDexterity:
cost = (CONSTANTS.ClassGymBaseCost * player.workCostMult) / gameCPS;
dexExp = (player.workExpMult / gameCPS) * hashManager.getTrainingMult();
break;
case CONSTANTS.ClassGymAgility:
cost = (CONSTANTS.ClassGymBaseCost * player.workCostMult) / gameCPS;
agiExp = (player.workExpMult / gameCPS) * hashManager.getTrainingMult();
break;
default:
throw new Error("ERR: Invalid/unrecognized class name");
}
return {
workMoneyLossRate: cost,
workHackExpGainRate: hackExp * player.hacking_exp_mult * BitNodeMultipliers.ClassGymExpGain,
workStrExpGainRate: strExp * player.strength_exp_mult * BitNodeMultipliers.ClassGymExpGain,
workDefExpGainRate: defExp * player.defense_exp_mult * BitNodeMultipliers.ClassGymExpGain,
workDexExpGainRate: dexExp * player.dexterity_exp_mult * BitNodeMultipliers.ClassGymExpGain,
workAgiExpGainRate: agiExp * player.agility_exp_mult * BitNodeMultipliers.ClassGymExpGain,
workChaExpGainRate: chaExp * player.charisma_exp_mult * BitNodeMultipliers.ClassGymExpGain,
};
}

@ -3,11 +3,29 @@ import { PlayerObject } from "./PersonObjects/Player/PlayerObject";
import { sanitizeExploits } from "./Exploits/Exploit";
import { Reviver } from "./utils/JSONReviver";
import Decimal from "decimal.js";
export let Player = new PlayerObject();
export function loadPlayer(saveString: string): void {
Player = JSON.parse(saveString, Reviver);
// Parse Decimal.js objects
Player.money = new Decimal(Player.money);
if (Player.corporation instanceof Corporation) {
Player.corporation.funds = new Decimal(Player.corporation.funds);
Player.corporation.revenue = new Decimal(Player.corporation.revenue);
Player.corporation.expenses = new Decimal(Player.corporation.expenses);
for (let i = 0; i < Player.corporation.divisions.length; ++i) {
const ind = Player.corporation.divisions[i];
ind.lastCycleRevenue = new Decimal(ind.lastCycleRevenue);
ind.lastCycleExpenses = new Decimal(ind.lastCycleExpenses);
ind.thisCycleRevenue = new Decimal(ind.thisCycleRevenue);
ind.thisCycleExpenses = new Decimal(ind.thisCycleExpenses);
}
}
Player.exploits = sanitizeExploits(Player.exploits);
}

@ -26,6 +26,7 @@ import { Terminal } from "./Terminal";
import { dialogBoxCreate } from "./ui/React/DialogBox";
import Decimal from "decimal.js";
import { ProgramsSeen } from "./Programs/ui/ProgramsRoot";
import { InvitationsSeen } from "./Faction/ui/FactionsRoot";
@ -119,7 +120,7 @@ export function prestigeAugmentation(): void {
// BitNode 8: Ghost of Wall Street
if (Player.bitNodeN === 8) {
Player.money = BitNode8StartingMoney;
Player.money = new Decimal(BitNode8StartingMoney);
}
if (Player.bitNodeN === 8 || SourceFileFlags[8] > 0) {
Player.hasWseAccount = true;
@ -234,7 +235,7 @@ export function prestigeSourceFile(flume: boolean): void {
// BitNode 8: Ghost of Wall Street
if (Player.bitNodeN === 8) {
Player.money = BitNode8StartingMoney;
Player.money = new Decimal(BitNode8StartingMoney);
}
if (Player.bitNodeN === 8 || SourceFileFlags[8] > 0) {
Player.hasWseAccount = true;

@ -306,10 +306,10 @@ export const programsMetadata: IProgramCreationParams[] = [
create: null,
run: (router: IRouter, terminal: ITerminal, player: IPlayer): void => {
const numAugReq = Math.round(BitNodeMultipliers.DaedalusAugsRequirement * 30);
const fulfilled = player.augmentations.length >= numAugReq && player.money > 1e11 && player.hacking >= 2500;
const fulfilled = player.augmentations.length >= numAugReq && player.money.gt(1e11) && player.hacking >= 2500;
if (!fulfilled) {
terminal.print(`Augmentations: ${player.augmentations.length} / ${numAugReq}`);
terminal.print(`Money: ${numeralWrapper.formatMoney(player.money)} / $100b`);
terminal.print(`Money: ${numeralWrapper.formatMoney(player.money.toNumber())} / $100b`);
terminal.print(`Hacking skill: ${player.hacking} / 2500`);
return;
}

@ -282,5 +282,6 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
Object.assign(Settings.theme, save.theme);
delete save.theme;
Object.assign(Settings, save);
console.log(Settings.TimestampsFormat);
},
};

@ -173,7 +173,7 @@ export function StockTicker(props: IProps): React.ReactElement {
}
function handleBuyMaxButtonClick(): void {
const playerMoney: number = props.p.money;
const playerMoney: number = props.p.money.toNumber();
const stock = props.stock;
let maxShares = calculateBuyMaxAmount(stock, position, playerMoney);

1
src/ThirdParty/decimal.js.d.ts vendored Normal file

@ -0,0 +1 @@
declare module "decimal.js";

@ -251,22 +251,6 @@ const Engine: {
const timeOffline = Engine._lastUpdate - lastUpdate;
const numCyclesOffline = Math.floor(timeOffline / CONSTANTS._idleSpeed);
// Generate coding contracts
let numContracts = 0;
if (numCyclesOffline < 3000 * 100) {
// if we have less than 100 rolls, just roll them exactly.
for (let i = 0; i < numCyclesOffline / 3000; i++) {
if (Math.random() < 0.25) numContracts++;
}
} else {
// just average it.
numContracts = (numCyclesOffline / 3000) * 0.25;
}
console.log(`${numCyclesOffline} ${numContracts}`);
for (let i = 0; i < numContracts; i++) {
generateRandomContract();
}
let offlineReputation = 0;
const offlineHackingIncome = (Player.moneySourceA.hacking / Player.playtimeSinceLastAug) * timeOffline * 0.75;
Player.gainMoney(offlineHackingIncome, "hacking");

@ -290,7 +290,7 @@ export function CharacterStats(): React.ReactElement {
<Employers />
<Typography>
Money: <Money money={player.money} />
Money: <Money money={player.money.toNumber()} />
<IconButton onClick={() => setMoneyOpen(true)}>
<MoreHorizIcon color="info" />
</IconButton>

@ -176,7 +176,9 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
<Typography classes={{ root: classes.money }}>Money&nbsp;</Typography>
</TableCell>
<TableCell align="right" classes={{ root: classes.cellNone }}>
<Typography classes={{ root: classes.money }}>{numeralWrapper.formatMoney(player.money)}</Typography>
<Typography classes={{ root: classes.money }}>
{numeralWrapper.formatMoney(player.money.toNumber())}
</Typography>
</TableCell>
<TableCell align="right" classes={{ root: classes.cellNone }}>
<Typography id="overview-money-hook" classes={{ root: classes.money }}>

@ -480,13 +480,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
<TextField
InputProps={{
startAdornment: (
<Typography
color={
formatTime(timestampFormat) === "format error" && timestampFormat !== ""
? "error"
: "success"
}
>
<Typography color={formatTime(timestampFormat) === "format error" ? "error" : "success"}>
Timestamp&nbsp;format:&nbsp;
</Typography>
),

@ -1,7 +1,6 @@
import { format } from "date-fns";
export function formatTime(fmt: string): string {
if (fmt === "") return "format error";
try {
return format(new Date(), fmt);
} catch (err: any) {