Revert "bunch of bugfixes"

This commit is contained in:
hydroflame 2021-11-11 20:06:05 -05:00 committed by GitHub
parent 22b915ad57
commit 70ffe25bb4
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", "better-react-mathjax": "^1.0.3",
"clsx": "^1.1.1", "clsx": "^1.1.1",
"date-fns": "^2.25.0", "date-fns": "^2.25.0",
"decimal.js": "7.2.3",
"escodegen": "^1.11.0", "escodegen": "^1.11.0",
"file-saver": "^1.3.8", "file-saver": "^1.3.8",
"jquery": "^3.5.0", "jquery": "^3.5.0",

@ -27,12 +27,12 @@ export function NewIndustry(corporation: ICorporation, industry: string, name: s
if (cost === undefined) { if (cost === undefined) {
throw new Error(`Invalid industry: '${industry}'`); 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"); throw new Error("Not enough money to create a new division in this industry");
} else if (name === "") { } else if (name === "") {
throw new Error("New division must have a name!"); throw new Error("New division must have a name!");
} else { } else {
corporation.funds = corporation.funds - cost; corporation.funds = corporation.funds.minus(cost);
corporation.divisions.push( corporation.divisions.push(
new Industry({ new Industry({
corp: corporation, 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 { 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!"); throw new Error("You don't have enough company funds to open a new office!");
} else { } else {
corporation.funds = corporation.funds - CorporationConstants.OfficeInitialCost; corporation.funds = corporation.funds.minus(CorporationConstants.OfficeInitialCost);
division.offices[city] = new OfficeSpace({ division.offices[city] = new OfficeSpace({
loc: city, loc: city,
size: CorporationConstants.OfficeInitialSize, size: CorporationConstants.OfficeInitialSize,
@ -56,7 +56,7 @@ export function NewCity(corporation: ICorporation, division: IIndustry, city: st
} }
export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnlockUpgrade): void { export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnlockUpgrade): void {
if (corporation.funds < upgrade[1]) { if (corporation.funds.lt(upgrade[1])) {
throw new Error("Insufficient funds"); throw new Error("Insufficient funds");
} }
corporation.unlock(upgrade); corporation.unlock(upgrade);
@ -67,7 +67,7 @@ export function LevelUpgrade(corporation: ICorporation, upgrade: CorporationUpgr
const priceMult = upgrade[2]; const priceMult = upgrade[2];
const level = corporation.upgrades[upgrade[0]]; const level = corporation.upgrades[upgrade[0]];
const cost = baseCost * Math.pow(priceMult, level); const cost = baseCost * Math.pow(priceMult, level);
if (corporation.funds < cost) { if (corporation.funds.lt(cost)) {
throw new Error("Insufficient funds"); throw new Error("Insufficient funds");
} else { } else {
corporation.upgrade(upgrade); corporation.upgrade(upgrade);
@ -259,15 +259,15 @@ export function UpgradeOfficeSize(corp: ICorporation, office: OfficeSpace, size:
mult += Math.pow(costMultiplier, initialPriceMult + i); mult += Math.pow(costMultiplier, initialPriceMult + i);
} }
const cost = CorporationConstants.OfficeInitialCost * mult; const cost = CorporationConstants.OfficeInitialCost * mult;
if (corp.funds < cost) return; if (corp.funds.lt(cost)) return;
office.size += size; office.size += size;
corp.funds = corp.funds - cost; corp.funds = corp.funds.minus(cost);
} }
export function ThrowParty(corp: ICorporation, office: OfficeSpace, costPerEmployee: number): number { export function ThrowParty(corp: ICorporation, office: OfficeSpace, costPerEmployee: number): number {
const totalCost = costPerEmployee * office.employees.length; const totalCost = costPerEmployee * office.employees.length;
if (corp.funds < totalCost) return 0; if (corp.funds.lt(totalCost)) return 0;
corp.funds = corp.funds - totalCost; corp.funds = corp.funds.minus(totalCost);
let mult = 0; let mult = 0;
for (let i = 0; i < office.employees.length; ++i) { for (let i = 0; i < office.employees.length; ++i) {
mult = office.employees[i].throwParty(costPerEmployee); 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 { 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; if (division.warehouses[city] instanceof Warehouse) return;
division.warehouses[city] = new Warehouse({ division.warehouses[city] = new Warehouse({
corp: corp, corp: corp,
@ -285,21 +285,21 @@ export function PurchaseWarehouse(corp: ICorporation, division: IIndustry, city:
loc: city, loc: city,
size: CorporationConstants.WarehouseInitialSize, 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 { export function UpgradeWarehouse(corp: ICorporation, division: IIndustry, warehouse: Warehouse): void {
const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1); const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1);
++warehouse.level; ++warehouse.level;
warehouse.updateSize(corp, division); 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 { export function BuyCoffee(corp: ICorporation, division: IIndustry, office: OfficeSpace): void {
const upgrade = IndustryUpgrades[0]; const upgrade = IndustryUpgrades[0];
const cost = office.employees.length * upgrade[1]; const cost = office.employees.length * upgrade[1];
if (corp.funds < cost) return; if (corp.funds.lt(cost)) return;
corp.funds = corp.funds - cost; corp.funds = corp.funds.minus(cost);
division.upgrade(upgrade, { division.upgrade(upgrade, {
corporation: corp, corporation: corp,
office: office, 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 { export function HireAdVert(corp: ICorporation, division: IIndustry, office: OfficeSpace): void {
const upgrade = IndustryUpgrades[1]; const upgrade = IndustryUpgrades[1];
const cost = upgrade[1] * Math.pow(upgrade[2], division.upgrades[1]); const cost = upgrade[1] * Math.pow(upgrade[2], division.upgrades[1]);
if (corp.funds < cost) return; if (corp.funds.lt(cost)) return;
corp.funds = corp.funds - cost; corp.funds = corp.funds.minus(cost);
division.upgrade(upgrade, { division.upgrade(upgrade, {
corporation: corp, corporation: corp,
office: office, office: office,
@ -340,7 +340,7 @@ export function MakeProduct(
if (isNaN(marketingInvest)) { if (isNaN(marketingInvest)) {
throw new Error("Invalid value for marketing investment"); 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"); throw new Error("You don't have enough company funds to make this large of an investment");
} }
const product = new Product({ const product = new Product({
@ -352,7 +352,7 @@ export function MakeProduct(
if (division.products[product.name] instanceof Product) { if (division.products[product.name] instanceof Product) {
throw new Error(`You already have a product with this name!`); 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; 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 { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
import { isString } from "../utils/helpers/isString"; import { isString } from "../utils/helpers/isString";
// UI Related Imports
import Decimal from "decimal.js";
interface IParams { interface IParams {
name?: string; name?: string;
} }
@ -25,9 +29,9 @@ export class Corporation {
divisions: Industry[] = []; divisions: Industry[] = [];
//Financial stats //Financial stats
funds = 150e9; funds = new Decimal(150e9);
revenue = 0; revenue = new Decimal(0);
expenses = 0; expenses = new Decimal(0);
fundingRound = 0; fundingRound = 0;
public = false; //Publicly traded public = false; //Publicly traded
totalShares = CorporationConstants.INITIALSHARES; // Total existing shares 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."); console.error("Trying to add invalid amount of funds. Report to a developper.");
return; return;
} }
this.funds = this.funds + amt; this.funds = this.funds.plus(amt);
} }
getState(): string { getState(): string {
@ -93,8 +97,8 @@ export class Corporation {
//At the start of a new cycle, calculate profits from previous cycle //At the start of a new cycle, calculate profits from previous cycle
if (state === "START") { if (state === "START") {
this.revenue = 0; this.revenue = new Decimal(0);
this.expenses = 0; this.expenses = new Decimal(0);
this.divisions.forEach((ind) => { this.divisions.forEach((ind) => {
if (ind.lastCycleRevenue === -Infinity || ind.lastCycleRevenue === Infinity) { if (ind.lastCycleRevenue === -Infinity || ind.lastCycleRevenue === Infinity) {
return; return;
@ -102,18 +106,18 @@ export class Corporation {
if (ind.lastCycleExpenses === -Infinity || ind.lastCycleExpenses === Infinity) { if (ind.lastCycleExpenses === -Infinity || ind.lastCycleExpenses === Infinity) {
return; return;
} }
this.revenue = this.revenue + ind.lastCycleRevenue; this.revenue = this.revenue.plus(ind.lastCycleRevenue);
this.expenses = this.expenses + ind.lastCycleExpenses; this.expenses = this.expenses.plus(ind.lastCycleExpenses);
}); });
const profit = this.revenue - this.expenses; const profit = this.revenue.minus(this.expenses);
const cycleProfit = profit * (marketCycles * CorporationConstants.SecsPerMarketCycle); const cycleProfit = profit.times(marketCycles * CorporationConstants.SecsPerMarketCycle);
if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) { if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) {
dialogBoxCreate( dialogBoxCreate(
"There was an error calculating your Corporations funds and they got reset to 0. " + "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>" + "This is a bug. Please report to game developer.<br><br>" +
"(Your funds have been set to $150b for the inconvenience)", "(Your funds have been set to $150b for the inconvenience)",
); );
this.funds = 150e9; this.funds = new Decimal(150e9);
} }
// Process dividends // Process dividends
@ -143,8 +147,8 @@ export class Corporation {
} }
getDividends(): number { getDividends(): number {
const profit = this.revenue - this.expenses; const profit = this.revenue.minus(this.expenses);
const cycleProfit = profit * CorporationConstants.SecsPerMarketCycle; const cycleProfit = profit.times(CorporationConstants.SecsPerMarketCycle);
const totalDividends = (this.dividendPercentage / 100) * cycleProfit; const totalDividends = (this.dividendPercentage / 100) * cycleProfit;
const dividendsPerShare = totalDividends / this.totalShares; const dividendsPerShare = totalDividends / this.totalShares;
const dividends = this.numShares * dividendsPerShare * (1 - this.dividendTaxPercentage / 100); const dividends = this.numShares * dividendsPerShare * (1 - this.dividendTaxPercentage / 100);
@ -153,18 +157,18 @@ export class Corporation {
determineValuation(): number { determineValuation(): number {
let val, let val,
profit = this.revenue - this.expenses; profit = this.revenue.minus(this.expenses).toNumber();
if (this.public) { if (this.public) {
// Account for dividends // Account for dividends
if (this.dividendPercentage > 0) { if (this.dividendPercentage > 0) {
profit *= (100 - this.dividendPercentage) / 100; 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.pow(1.1, this.divisions.length);
val = Math.max(val, 0); val = Math.max(val, 0);
} else { } 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) { if (profit > 0) {
val += profit * 315e3; val += profit * 315e3;
val *= Math.pow(1.1, this.divisions.length); val *= Math.pow(1.1, this.divisions.length);
@ -258,12 +262,12 @@ export class Corporation {
while (this.unlockUpgrades.length <= upgN) { while (this.unlockUpgrades.length <= upgN) {
this.unlockUpgrades.push(0); this.unlockUpgrades.push(0);
} }
if (this.funds < price) { if (this.funds.lt(price)) {
dialogBoxCreate("You don't have enough funds to unlock this!"); dialogBoxCreate("You don't have enough funds to unlock this!");
return; return;
} }
this.unlockUpgrades[upgN] = 1; this.unlockUpgrades[upgN] = 1;
this.funds = this.funds - price; this.funds = this.funds.minus(price);
// Apply effects for one-time upgrades // Apply effects for one-time upgrades
if (upgN === 5) { if (upgN === 5) {
@ -286,12 +290,12 @@ export class Corporation {
this.upgradeMultipliers.push(1); this.upgradeMultipliers.push(1);
} }
const totalCost = basePrice * Math.pow(priceMult, this.upgrades[upgN]); 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!"); dialogBoxCreate("You don't have enough funds to purchase this!");
return; return;
} }
++this.upgrades[upgN]; ++this.upgrades[upgN];
this.funds = this.funds - totalCost; this.funds = this.funds.minus(totalCost);
//Increase upgrade multiplier //Increase upgrade multiplier
this.upgradeMultipliers[upgN] = 1 + this.upgrades[upgN] * upgradeAmt; this.upgradeMultipliers[upgN] = 1 + this.upgrades[upgN] * upgradeAmt;

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

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

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

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

@ -32,7 +32,7 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
if (cost === undefined) { if (cost === undefined) {
throw new Error(`Invalid industry: '${industry}'`); throw new Error(`Invalid industry: '${industry}'`);
} }
const disabled = corp.funds < cost || name === ""; const disabled = corp.funds.lt(cost) || name === "";
function newIndustry(): void { function newIndustry(): void {
if (disabled) return; 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 possibleCities = Object.keys(division.offices).filter((cityName: string) => division.offices[cityName] === 0);
const [city, setCity] = useState(possibleCities[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 { function onCityChange(event: SelectChangeEvent<string>): void {
setCity(event.target.value); setCity(event.target.value);

@ -440,7 +440,7 @@ export function IndustryOffice(props: IProps): React.ReactElement {
<br /> <br />
<Tooltip title={<Typography>Upgrade the office's size so that it can hold more employees!</Typography>}> <Tooltip title={<Typography>Upgrade the office's size so that it can hold more employees!</Typography>}>
<span> <span>
<Button disabled={corp.funds < 0} onClick={() => setUpgradeOfficeSizeOpen(true)}> <Button disabled={corp.funds.lt(0)} onClick={() => setUpgradeOfficeSizeOpen(true)}>
Upgrade size Upgrade size
</Button> </Button>
</span> </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>} title={<Typography>Throw an office party to increase your employee's morale and happiness</Typography>}
> >
<span> <span>
<Button disabled={corp.funds < 0} onClick={() => setThrowPartyOpen(true)}> <Button disabled={corp.funds.lt(0)} onClick={() => setThrowPartyOpen(true)}>
Throw Party Throw Party
</Button> </Button>
</span> </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} {createProductButtonText}
</Button> </Button>
</Tooltip> </Tooltip>
@ -93,7 +97,7 @@ function Text(): React.ReactElement {
const [helpOpen, setHelpOpen] = useState(false); const [helpOpen, setHelpOpen] = useState(false);
const [researchOpen, setResearchOpen] = useState(false); const [researchOpen, setResearchOpen] = useState(false);
const vechain = corp.unlockUpgrades[4] === 1; const vechain = corp.unlockUpgrades[4] === 1;
const profit = division.lastCycleRevenue - division.lastCycleExpenses; const profit = division.lastCycleRevenue.minus(division.lastCycleExpenses).toNumber();
let advertisingInfo = false; let advertisingInfo = false;
const advertisingFactors = division.getAdvertisingFactors(); const advertisingFactors = division.getAdvertisingFactors();
@ -115,7 +119,7 @@ function Text(): React.ReactElement {
return ( return (
<> <>
<Typography> <Typography>
Industry: {division.type} (Corp Funds: <Money money={corp.funds} />) Industry: {division.type} (Corp Funds: <Money money={corp.funds.toNumber()} />)
</Typography> </Typography>
<br /> <br />
<StatsTable <StatsTable
@ -145,8 +149,8 @@ function Text(): React.ReactElement {
<br /> <br />
<StatsTable <StatsTable
rows={[ rows={[
["Revenue:", <MoneyRate money={division.lastCycleRevenue} />], ["Revenue:", <MoneyRate money={division.lastCycleRevenue.toNumber()} />],
["Expenses:", <MoneyRate money={division.lastCycleExpenses} />], ["Expenses:", <MoneyRate money={division.lastCycleExpenses.toNumber()} />],
["Profit:", <MoneyRate money={profit} />], ["Profit:", <MoneyRate money={profit} />],
]} ]}
/> />
@ -237,8 +241,8 @@ function Upgrades(props: { office: OfficeSpace; rerender: () => void }): React.R
} }
function onClick(): void { function onClick(): void {
if (corp.funds < cost) return; if (corp.funds.lt(cost)) return;
corp.funds = corp.funds - cost; corp.funds = corp.funds.minus(cost);
division.upgrade(upgrade, { division.upgrade(upgrade, {
corporation: corp, corporation: corp,
office: props.office, office: props.office,
@ -249,7 +253,7 @@ function Upgrades(props: { office: OfficeSpace; rerender: () => void }): React.R
upgrades.push( upgrades.push(
<Tooltip key={index} title={upgrade[5]}> <Tooltip key={index} title={upgrade[5]}>
<span> <span>
<Button disabled={corp.funds < cost} onClick={onClick}> <Button disabled={corp.funds.lt(cost)} onClick={onClick}>
{upgrade[4]} -&nbsp; {upgrade[4]} -&nbsp;
<MoneyCost money={cost} corp={corp} /> <MoneyCost money={cost} corp={corp} />
</Button> </Button>

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

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

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

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

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

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

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

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

@ -19,13 +19,13 @@ interface IProps {
export function Corporation(props: IProps): React.ReactElement { export function Corporation(props: IProps): React.ReactElement {
function addTonsCorporationFunds(): void { function addTonsCorporationFunds(): void {
if (props.player.corporation) { 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 { function resetCorporationFunds(): void {
if (props.player.corporation) { 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 repCost = aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
const hasReq = props.faction.playerReputation >= repCost; const hasReq = props.faction.playerReputation >= repCost;
const hasRep = hasAugmentationPrereqs(aug); 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; return hasCost && hasReq && hasRep;
} }
const buy = augs.filter(canBuy).sort((augName1, augName2) => { const buy = augs.filter(canBuy).sort((augName1, augName2) => {

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

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

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

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

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

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

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

@ -2,11 +2,18 @@ import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
export function getHospitalizationCost(p: IPlayer): number { 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 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 { export function calculateHospitalizationCost(p: IPlayer, damage: number): number {

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

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

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

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

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

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

@ -174,9 +174,9 @@ export function SleeveElem(props: IProps): React.ReactElement {
<Grid item xs={3}> <Grid item xs={3}>
<StatsElement sleeve={props.sleeve} /> <StatsElement sleeve={props.sleeve} />
<Button onClick={() => setStatsOpen(true)}>More Stats</Button> <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> <span>
<Button onClick={() => setTravelOpen(true)} disabled={player.money < CONSTANTS.TravelCost}> <Button onClick={() => setTravelOpen(true)} disabled={player.money.lt(CONSTANTS.TravelCost)}>
Travel Travel
</Button> </Button>
</span> </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 { sanitizeExploits } from "./Exploits/Exploit";
import { Reviver } from "./utils/JSONReviver"; import { Reviver } from "./utils/JSONReviver";
import Decimal from "decimal.js";
export let Player = new PlayerObject(); export let Player = new PlayerObject();
export function loadPlayer(saveString: string): void { export function loadPlayer(saveString: string): void {
Player = JSON.parse(saveString, Reviver); 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); Player.exploits = sanitizeExploits(Player.exploits);
} }

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

@ -306,10 +306,10 @@ export const programsMetadata: IProgramCreationParams[] = [
create: null, create: null,
run: (router: IRouter, terminal: ITerminal, player: IPlayer): void => { run: (router: IRouter, terminal: ITerminal, player: IPlayer): void => {
const numAugReq = Math.round(BitNodeMultipliers.DaedalusAugsRequirement * 30); 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) { if (!fulfilled) {
terminal.print(`Augmentations: ${player.augmentations.length} / ${numAugReq}`); 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`); terminal.print(`Hacking skill: ${player.hacking} / 2500`);
return; return;
} }

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

@ -173,7 +173,7 @@ export function StockTicker(props: IProps): React.ReactElement {
} }
function handleBuyMaxButtonClick(): void { function handleBuyMaxButtonClick(): void {
const playerMoney: number = props.p.money; const playerMoney: number = props.p.money.toNumber();
const stock = props.stock; const stock = props.stock;
let maxShares = calculateBuyMaxAmount(stock, position, playerMoney); 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 timeOffline = Engine._lastUpdate - lastUpdate;
const numCyclesOffline = Math.floor(timeOffline / CONSTANTS._idleSpeed); 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; let offlineReputation = 0;
const offlineHackingIncome = (Player.moneySourceA.hacking / Player.playtimeSinceLastAug) * timeOffline * 0.75; const offlineHackingIncome = (Player.moneySourceA.hacking / Player.playtimeSinceLastAug) * timeOffline * 0.75;
Player.gainMoney(offlineHackingIncome, "hacking"); Player.gainMoney(offlineHackingIncome, "hacking");

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

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

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

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