This commit is contained in:
Olivier Gagnon
2021-11-11 20:14:06 -05:00
53 changed files with 316 additions and 345 deletions

View File

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

View File

@@ -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);
@@ -161,18 +165,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);
@@ -266,12 +270,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) {
@@ -294,12 +298,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;

View File

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

View File

@@ -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[];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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"],

View File

@@ -25,25 +25,15 @@ 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>
Purchasing {numeralWrapper.format(parsedAmt, "0,0.00")} of {props.mat.name} will cost{" "}
{numeralWrapper.formatMoney(cost)}
</Typography>
</>
<><Typography>
Purchasing {numeralWrapper.format(parsedAmt, "0,0.00")} of {props.mat.name} will cost{" "}
{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.`);

View File

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

View File

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

View File

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

View File

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

View File

@@ -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) => {

View File

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

View File

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

View File

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

View File

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

View File

@@ -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": {

View File

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

View File

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

View File

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

View File

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

View File

@@ -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}'`,

View File

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

View File

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

View File

@@ -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 = "";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@@ -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");

View File

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

View File

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

View File

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

View File

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