track money more precisely.

This commit is contained in:
Olivier Gagnon 2021-10-27 14:18:33 -04:00
parent 9f6767062b
commit 48988e228e
38 changed files with 90 additions and 100 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1235,8 +1235,7 @@ export class Bladeburner implements IBladeburner {
let moneyGain = 0; let moneyGain = 0;
if (!isOperation) { if (!isOperation) {
moneyGain = BladeburnerConstants.ContractBaseMoneyGain * rewardMultiplier * this.skillMultipliers.money; moneyGain = BladeburnerConstants.ContractBaseMoneyGain * rewardMultiplier * this.skillMultipliers.money;
player.gainMoney(moneyGain); player.gainMoney(moneyGain, "bladeburner");
player.recordMoneySource(moneyGain, "bladeburner");
} }
if (isOperation) { if (isOperation) {

@ -76,7 +76,7 @@ export class Blackjack extends Game<Props, State> {
// Take money from player right away so that player's dont just "leave" to avoid the loss (I mean they could // Take money from player right away so that player's dont just "leave" to avoid the loss (I mean they could
// always reload without saving but w.e) // always reload without saving but w.e)
this.props.p.loseMoney(this.state.bet); this.props.p.loseMoney(this.state.bet, "casino");
const playerHand = new Hand([this.deck.safeDrawCard(), this.deck.safeDrawCard()]); const playerHand = new Hand([this.deck.safeDrawCard(), this.deck.safeDrawCard()]);
const dealerHand = new Hand([this.deck.safeDrawCard(), this.deck.safeDrawCard()]); const dealerHand = new Hand([this.deck.safeDrawCard(), this.deck.safeDrawCard()]);

@ -5,8 +5,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
const gainLimit = 10e9; const gainLimit = 10e9;
export function win(p: IPlayer, n: number): void { export function win(p: IPlayer, n: number): void {
p.gainMoney(n); p.gainMoney(n, "casino");
p.recordMoneySource(n, "casino");
} }
export function reachedLimit(p: IPlayer): boolean { export function reachedLimit(p: IPlayer): boolean {
@ -19,8 +18,7 @@ export function reachedLimit(p: IPlayer): boolean {
export class Game<T, U> extends React.Component<T, U> { export class Game<T, U> extends React.Component<T, U> {
win(p: IPlayer, n: number): void { win(p: IPlayer, n: number): void {
p.gainMoney(n); p.gainMoney(n, "casino");
p.recordMoneySource(n, "casino");
} }
reachedLimit(p: IPlayer): boolean { reachedLimit(p: IPlayer): boolean {

@ -134,8 +134,7 @@ export class Corporation {
const retainedEarnings = cycleProfit - totalDividends; const retainedEarnings = cycleProfit - totalDividends;
const dividendsPerShare = totalDividends / this.totalShares; const dividendsPerShare = totalDividends / this.totalShares;
const profit = this.numShares * dividendsPerShare * (1 - this.dividendTaxPercentage / 100); const profit = this.numShares * dividendsPerShare * (1 - this.dividendTaxPercentage / 100);
player.gainMoney(profit); player.gainMoney(profit, "corporation");
player.recordMoneySource(profit, "corporation");
this.addFunds(retainedEarnings); this.addFunds(retainedEarnings);
} }
} else { } else {

@ -48,7 +48,7 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
} }
} }
corp.issuedShares -= shares; corp.issuedShares -= shares;
player.loseMoney(shares * buybackPrice); player.loseMoney(shares * buybackPrice, "corporation");
props.onClose(); props.onClose();
props.rerender(); props.rerender();
} }

@ -36,7 +36,7 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
} }
player.startCorporation(name); player.startCorporation(name);
player.loseMoney(150e9); player.loseMoney(150e9, "corporation");
props.onClose(); props.onClose();
router.toCorporation(); router.toCorporation();

@ -69,8 +69,7 @@ export function SellSharesModal(props: IProps): React.ReactElement {
corp.sharePrice = newSharePrice; corp.sharePrice = newSharePrice;
corp.shareSalesUntilPriceUpdate = newSharesUntilUpdate; corp.shareSalesUntilPriceUpdate = newSharesUntilUpdate;
corp.shareSaleCooldown = CorporationConstants.SellSharesCooldown; corp.shareSaleCooldown = CorporationConstants.SellSharesCooldown;
player.gainMoney(profit); player.gainMoney(profit, "corporation");
player.recordMoneySource(profit, "corporation");
props.onClose(); props.onClose();
dialogBoxCreate( dialogBoxCreate(
<> <>

@ -60,7 +60,7 @@ export function buyDarkwebItem(itemName: string): void {
} }
// buy and push // buy and push
Player.loseMoney(item.price); Player.loseMoney(item.price, "other");
Player.getHomeComputer().programs.push(item.program); Player.getHomeComputer().programs.push(item.program);
Terminal.print( Terminal.print(
"You have purchased the " + item.program + " program. The new program can be found on your home computer.", "You have purchased the " + item.program + " program. The new program can be found on your home computer.",

@ -19,7 +19,7 @@ interface IProps {
export function General(props: IProps): React.ReactElement { export function General(props: IProps): React.ReactElement {
function addMoney(n: number) { function addMoney(n: number) {
return function () { return function () {
props.player.gainMoney(n); props.player.gainMoney(n, "other");
}; };
} }

@ -106,7 +106,7 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
} }
Player.queuedAugmentations.push(queuedAugmentation); Player.queuedAugmentations.push(queuedAugmentation);
Player.loseMoney(aug.baseCost * factionInfo.augmentationPriceMult); Player.loseMoney(aug.baseCost * factionInfo.augmentationPriceMult, "augmentations");
// If you just purchased Neuroflux Governor, recalculate the cost // If you just purchased Neuroflux Governor, recalculate the cost
if (aug.name == AugmentationNames.NeuroFluxGovernor) { if (aug.name == AugmentationNames.NeuroFluxGovernor) {

@ -52,7 +52,7 @@ export function DonateOption(props: IProps): React.ReactElement {
const amt = donateAmt; const amt = donateAmt;
if (amt === null) return; if (amt === null) return;
if (!canDonate()) return; if (!canDonate()) return;
props.p.loseMoney(amt); props.p.loseMoney(amt, "other");
const repGain = repFromDonation(amt, props.p); const repGain = repFromDonation(amt, props.p);
props.faction.playerReputation += repGain; props.faction.playerReputation += repGain;
dialogBoxCreate( dialogBoxCreate(

@ -147,8 +147,7 @@ export class Gang {
this.wanted = newWanted; this.wanted = newWanted;
if (this.wanted < 1) this.wanted = 1; if (this.wanted < 1) this.wanted = 1;
} }
player.gainMoney(moneyGains * numCycles); player.gainMoney(moneyGains * numCycles, "gang");
player.recordMoneySource(moneyGains * numCycles, "gang");
} }
processTerritoryAndPowerGains(numCycles = 1): void { processTerritoryAndPowerGains(numCycles = 1): void {

@ -317,7 +317,7 @@ export class GangMember {
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.lt(gang.getUpgradeCost(upg))) return false; if (player.money.lt(gang.getUpgradeCost(upg))) return false;
player.loseMoney(gang.getUpgradeCost(upg)); player.loseMoney(gang.getUpgradeCost(upg), "gang");
if (upg.type === "g") { if (upg.type === "g") {
this.augmentations.push(upg.name); this.augmentations.push(upg.name);
} else { } else {

@ -50,7 +50,7 @@ export function purchaseHacknet(player: IPlayer): number {
if (!player.canAfford(cost)) { if (!player.canAfford(cost)) {
return -1; return -1;
} }
player.loseMoney(cost); player.loseMoney(cost, "hacknet");
player.createHacknetServer(); player.createHacknetServer();
updateHashManagerCapacity(player); updateHashManagerCapacity(player);
@ -69,7 +69,7 @@ export function purchaseHacknet(player: IPlayer): number {
const name = "hacknet-node-" + numOwned; const name = "hacknet-node-" + numOwned;
const node = new HacknetNode(name, player.hacknet_node_money_mult); const node = new HacknetNode(name, player.hacknet_node_money_mult);
player.loseMoney(cost); player.loseMoney(cost, "hacknet");
player.hacknetNodes.push(node); player.hacknetNodes.push(node);
return numOwned; return numOwned;
@ -266,7 +266,7 @@ export function purchaseLevelUpgrade(player: IPlayer, node: HacknetNode | Hackne
return false; return false;
} }
player.loseMoney(cost); player.loseMoney(cost, "hacknet");
node.upgradeLevel(sanitizedLevels, player.hacknet_node_money_mult); node.upgradeLevel(sanitizedLevels, player.hacknet_node_money_mult);
return true; return true;
@ -305,7 +305,7 @@ export function purchaseRamUpgrade(player: IPlayer, node: HacknetNode | HacknetS
return false; return false;
} }
player.loseMoney(cost); player.loseMoney(cost, "hacknet");
node.upgradeRam(sanitizedLevels, player.hacknet_node_money_mult); node.upgradeRam(sanitizedLevels, player.hacknet_node_money_mult);
return true; return true;
@ -336,7 +336,7 @@ export function purchaseCoreUpgrade(player: IPlayer, node: HacknetNode | Hacknet
return false; return false;
} }
player.loseMoney(cost); player.loseMoney(cost, "hacknet");
node.upgradeCore(sanitizedLevels, player.hacknet_node_money_mult); node.upgradeCore(sanitizedLevels, player.hacknet_node_money_mult);
return true; return true;
@ -364,7 +364,7 @@ export function purchaseCacheUpgrade(player: IPlayer, node: HacknetServer, level
return false; return false;
} }
player.loseMoney(cost); player.loseMoney(cost, "hacknet");
node.upgradeCache(sanitizedLevels); node.upgradeCache(sanitizedLevels);
return true; return true;
@ -398,8 +398,7 @@ function processAllHacknetNodeEarnings(player: IPlayer, numCycles: number): numb
function processSingleHacknetNodeEarnings(player: IPlayer, numCycles: number, nodeObj: HacknetNode): number { function processSingleHacknetNodeEarnings(player: IPlayer, numCycles: number, nodeObj: HacknetNode): number {
const totalEarnings = nodeObj.process(numCycles); const totalEarnings = nodeObj.process(numCycles);
player.gainMoney(totalEarnings); player.gainMoney(totalEarnings, "hacknet");
player.recordMoneySource(totalEarnings, "hacknetnode");
return totalEarnings; return totalEarnings;
} }
@ -472,8 +471,7 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
switch (upgName) { switch (upgName) {
case "Sell for Money": { case "Sell for Money": {
player.gainMoney(upg.value); player.gainMoney(upg.value, "hacknet");
player.recordMoneySource(upg.value, "hacknetnode");
break; break;
} }
case "Sell for Corporation Funds": { case "Sell for Corporation Funds": {

@ -42,8 +42,7 @@ export function Victory(props: IProps): React.ReactElement {
BitNodeMultipliers.InfiltrationMoney; BitNodeMultipliers.InfiltrationMoney;
function sell(): void { function sell(): void {
player.gainMoney(moneyGain); player.gainMoney(moneyGain, "infiltration");
player.recordMoneySource(moneyGain, "infiltration");
quitInfiltration(); quitInfiltration();
} }

@ -23,7 +23,7 @@ export function purchaseTorRouter(p: IPlayer): void {
dialogBoxCreate("You cannot afford to purchase the TOR router!"); dialogBoxCreate("You cannot afford to purchase the TOR router!");
return; return;
} }
p.loseMoney(CONSTANTS.TorRouterCost); p.loseMoney(CONSTANTS.TorRouterCost, "other");
const darkweb = safetlyCreateUniqueServer({ const darkweb = safetlyCreateUniqueServer({
ip: createUniqueRandomIp(), ip: createUniqueRandomIp(),

@ -24,7 +24,7 @@ export function CoresButton(props: IProps): React.ReactElement {
function buy(): void { function buy(): void {
if (maxCores) return; if (maxCores) return;
if (!props.p.canAfford(cost)) return; if (!props.p.canAfford(cost)) return;
props.p.loseMoney(cost); props.p.loseMoney(cost, "servers");
homeComputer.cpuCores++; homeComputer.cpuCores++;
props.rerender(); props.rerender();
} }

@ -57,9 +57,8 @@ export class HospitalLocation extends React.Component<IProps, IState> {
} }
const cost = this.getCost(); const cost = this.getCost();
this.props.p.loseMoney(cost); this.props.p.loseMoney(cost, "hospitalization");
this.props.p.hp = this.props.p.max_hp; this.props.p.hp = this.props.p.max_hp;
this.props.p.recordMoneySource(-1 * cost, "hospitalization");
// This just forces a re-render to update the cost // This just forces a re-render to update the cost
this.setState({ this.setState({

@ -33,7 +33,7 @@ function travel(p: IPlayer, router: IRouter, to: CityName): void {
return; return;
} }
p.loseMoney(cost); p.loseMoney(cost, "other");
p.travel(to); p.travel(to);
dialogBoxCreate(<>You are now in {to}!</>); dialogBoxCreate(<>You are now in {to}!</>);
router.toCity(); router.toCity();

@ -395,10 +395,9 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
const moneyGained = moneyDrained * BitNodeMultipliers.ScriptHackMoneyGain; const moneyGained = moneyDrained * BitNodeMultipliers.ScriptHackMoneyGain;
Player.gainMoney(moneyGained); Player.gainMoney(moneyGained, "hacking");
workerScript.scriptRef.onlineMoneyMade += moneyGained; workerScript.scriptRef.onlineMoneyMade += moneyGained;
Player.scriptProdSinceLastAug += moneyGained; Player.scriptProdSinceLastAug += moneyGained;
Player.recordMoneySource(moneyGained, "hacking");
workerScript.scriptRef.recordHack(server.hostname, moneyGained, threads); workerScript.scriptRef.recordHack(server.hostname, moneyGained, threads);
Player.gainHackingExp(expGainedOnSuccess); Player.gainHackingExp(expGainedOnSuccess);
workerScript.scriptRef.onlineExpGained += expGainedOnSuccess; workerScript.scriptRef.onlineExpGained += expGainedOnSuccess;
@ -1550,7 +1549,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
const homeComputer = Player.getHomeComputer(); const homeComputer = Player.getHomeComputer();
homeComputer.serversOnNetwork.push(newServ.hostname); homeComputer.serversOnNetwork.push(newServ.hostname);
newServ.serversOnNetwork.push(homeComputer.hostname); newServ.serversOnNetwork.push(homeComputer.hostname);
Player.loseMoney(cost); Player.loseMoney(cost, "servers");
workerScript.log( workerScript.log(
"purchaseServer", "purchaseServer",
`Purchased new server with hostname '${newServ.hostname}' for ${numeralWrapper.formatMoney(cost)}`, `Purchased new server with hostname '${newServ.hostname}' for ${numeralWrapper.formatMoney(cost)}`,
@ -2297,7 +2296,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (Player.money.lt(CONSTANTS.TravelCost)) { if (Player.money.lt(CONSTANTS.TravelCost)) {
throw makeRuntimeErrorMsg("travelToCity", "Not enough money to travel."); throw makeRuntimeErrorMsg("travelToCity", "Not enough money to travel.");
} }
Player.loseMoney(CONSTANTS.TravelCost); Player.loseMoney(CONSTANTS.TravelCost, "other");
Player.city = cityname; Player.city = cityname;
workerScript.log("travelToCity", `Traveled to ${cityname}`); workerScript.log("travelToCity", `Traveled to ${cityname}`);
return true; return true;
@ -2320,7 +2319,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
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;
} }
Player.loseMoney(CONSTANTS.TorRouterCost); Player.loseMoney(CONSTANTS.TorRouterCost, "other");
const darkweb = safetlyCreateUniqueServer({ const darkweb = safetlyCreateUniqueServer({
ip: createUniqueRandomIp(), ip: createUniqueRandomIp(),
@ -2376,7 +2375,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
return true; return true;
} }
Player.loseMoney(item.price); Player.loseMoney(item.price, "other");
Player.getHomeComputer().programs.push(item.program); Player.getHomeComputer().programs.push(item.program);
workerScript.log( workerScript.log(
"purchaseProgram", "purchaseProgram",
@ -2667,7 +2666,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
} }
homeComputer.cpuCores += 1; homeComputer.cpuCores += 1;
Player.loseMoney(cost); Player.loseMoney(cost, "servers");
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain); Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
workerScript.log( workerScript.log(
@ -2700,7 +2699,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
} }
homeComputer.maxRam *= 2; homeComputer.maxRam *= 2;
Player.loseMoney(cost); Player.loseMoney(cost, "servers");
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain); Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
workerScript.log( workerScript.log(
@ -3061,7 +3060,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
} }
const repGain = (amt / CONSTANTS.DonateMoneyToRepDivisor) * Player.faction_rep_mult; const repGain = (amt / CONSTANTS.DonateMoneyToRepDivisor) * Player.faction_rep_mult;
faction.playerReputation += repGain; faction.playerReputation += repGain;
Player.loseMoney(amt); Player.loseMoney(amt, "other");
workerScript.log( workerScript.log(
"donateToFaction", "donateToFaction",
`${numeralWrapper.formatMoney(amt)} donated to '${name}' for ${numeralWrapper.formatReputation( `${numeralWrapper.formatMoney(amt)} donated to '${name}' for ${numeralWrapper.formatReputation(

@ -350,7 +350,7 @@ export function NetscriptStockMarket(
} }
player.has4SData = true; player.has4SData = true;
player.loseMoney(getStockMarket4SDataCost()); player.loseMoney(getStockMarket4SDataCost(), "stock");
workerScript.log("purchase4SMarketData", "Purchased 4S Market Data"); workerScript.log("purchase4SMarketData", "Purchased 4S Market Data");
return true; return true;
}, },
@ -369,7 +369,7 @@ export function NetscriptStockMarket(
} }
player.has4SDataTixApi = true; player.has4SDataTixApi = true;
player.loseMoney(getStockMarket4STixApiCost()); player.loseMoney(getStockMarket4STixApiCost(), "stock");
workerScript.log("purchase4SMarketDataTixApi", "Purchased 4S Market Data TIX API"); workerScript.log("purchase4SMarketDataTixApi", "Purchased 4S Market Data TIX API");
return true; return true;
}, },

@ -184,7 +184,7 @@ export interface IPlayer {
gainAgilityExp(exp: number): void; gainAgilityExp(exp: number): void;
gainCharismaExp(exp: number): void; gainCharismaExp(exp: number): void;
gainIntelligenceExp(exp: number): void; gainIntelligenceExp(exp: number): void;
gainMoney(money: number): void; gainMoney(money: number, source: string): void;
getCurrentServer(): BaseServer; getCurrentServer(): BaseServer;
getGangFaction(): Faction; getGangFaction(): Faction;
getGangName(): string; getGangName(): string;
@ -201,12 +201,11 @@ export interface IPlayer {
inBladeburner(): boolean; inBladeburner(): boolean;
inGang(): boolean; inGang(): boolean;
isQualified(company: Company, position: CompanyPosition): boolean; isQualified(company: Company, position: CompanyPosition): boolean;
loseMoney(money: number): void; loseMoney(money: number, source: string): void;
process(router: IRouter, numCycles?: number): void; process(router: IRouter, numCycles?: number): void;
reapplyAllAugmentations(resetMultipliers?: boolean): void; reapplyAllAugmentations(resetMultipliers?: boolean): void;
reapplyAllSourceFiles(): void; reapplyAllSourceFiles(): void;
regenerateHp(amt: number): void; regenerateHp(amt: number): void;
recordMoneySource(amt: number, source: string): void;
setMoney(amt: number): void; setMoney(amt: number): void;
singularityStopWork(): string; singularityStopWork(): string;
startBladeburner(p: any): void; startBladeburner(p: any): void;

@ -191,7 +191,7 @@ export class PlayerObject implements IPlayer {
gainAgilityExp: (exp: number) => void; gainAgilityExp: (exp: number) => void;
gainCharismaExp: (exp: number) => void; gainCharismaExp: (exp: number) => void;
gainIntelligenceExp: (exp: number) => void; gainIntelligenceExp: (exp: number) => void;
gainMoney: (money: number) => void; gainMoney: (money: number, source: string) => void;
getCurrentServer: () => BaseServer; getCurrentServer: () => BaseServer;
getGangFaction: () => Faction; getGangFaction: () => Faction;
getGangName: () => string; getGangName: () => string;
@ -208,7 +208,7 @@ export class PlayerObject implements IPlayer {
inBladeburner: () => boolean; inBladeburner: () => boolean;
inGang: () => boolean; inGang: () => boolean;
isQualified: (company: Company, position: CompanyPosition) => boolean; isQualified: (company: Company, position: CompanyPosition) => boolean;
loseMoney: (money: number) => void; loseMoney: (money: number, source: string) => void;
reapplyAllAugmentations: (resetMultipliers?: boolean) => void; reapplyAllAugmentations: (resetMultipliers?: boolean) => void;
reapplyAllSourceFiles: () => void; reapplyAllSourceFiles: () => void;
regenerateHp: (amt: number) => void; regenerateHp: (amt: number) => void;

@ -328,21 +328,23 @@ export function setMoney(this: PlayerObject, money: number): void {
this.money = new Decimal(money); this.money = new Decimal(money);
} }
export function gainMoney(this: PlayerObject, money: number): void { export function gainMoney(this: PlayerObject, money: number, source: string): void {
if (isNaN(money)) { if (isNaN(money)) {
console.error("NaN passed into Player.gainMoney()"); console.error("NaN passed into Player.gainMoney()");
return; return;
} }
this.money = this.money.plus(money); this.money = this.money.plus(money);
this.recordMoneySource(money, source);
} }
export function loseMoney(this: PlayerObject, money: number): void { export function loseMoney(this: PlayerObject, money: number, source: string): void {
if (isNaN(money)) { if (isNaN(money)) {
console.error("NaN passed into Player.loseMoney()"); console.error("NaN passed into Player.loseMoney()");
return; return;
} }
if (this.money.eq(Infinity) && money === Infinity) return; if (this.money.eq(Infinity) && money === Infinity) return;
this.money = this.money.minus(money); this.money = this.money.minus(money);
this.recordMoneySource(-1 * money, source);
} }
export function canAfford(this: IPlayer, cost: number): boolean { export function canAfford(this: IPlayer, cost: number): boolean {
@ -353,7 +355,7 @@ export function canAfford(this: IPlayer, cost: number): boolean {
return this.money.gte(cost); return this.money.gte(cost);
} }
export function recordMoneySource(this: IPlayer, amt: number, source: string): void { export function recordMoneySource(this: PlayerObject, amt: number, source: string): void {
if (!(this.moneySourceA instanceof MoneySourceTracker)) { if (!(this.moneySourceA instanceof MoneySourceTracker)) {
console.warn(`Player.moneySourceA was not properly initialized. Resetting`); console.warn(`Player.moneySourceA was not properly initialized. Resetting`);
this.moneySourceA = new MoneySourceTracker(); this.moneySourceA = new MoneySourceTracker();
@ -540,12 +542,7 @@ export function processWorkEarnings(this: IPlayer, numCycles = 1): void {
this.gainDexterityExp(dexExpGain); this.gainDexterityExp(dexExpGain);
this.gainAgilityExp(agiExpGain); this.gainAgilityExp(agiExpGain);
this.gainCharismaExp(chaExpGain); this.gainCharismaExp(chaExpGain);
this.gainMoney(moneyGain); this.gainMoney(moneyGain, this.className ? "class" : "work");
if (this.className) {
this.recordMoneySource(moneyGain, "class");
} else {
this.recordMoneySource(moneyGain, "work");
}
this.workHackExpGained += hackExpGain; this.workHackExpGained += hackExpGain;
this.workStrExpGained += strExpGain; this.workStrExpGained += strExpGain;
this.workDefExpGained += defExpGain; this.workDefExpGained += defExpGain;
@ -1513,8 +1510,7 @@ export function finishCrime(this: IPlayer, cancelled: boolean): string {
); );
return ""; return "";
} }
this.gainMoney(this.workMoneyGained); this.gainMoney(this.workMoneyGained, "crime");
this.recordMoneySource(this.workMoneyGained, "crime");
this.karma -= crime.karma; this.karma -= crime.karma;
this.numPeopleKilled += crime.kills; this.numPeopleKilled += crime.kills;
if (crime.intelligence_exp > 0) { if (crime.intelligence_exp > 0) {
@ -1703,8 +1699,7 @@ export function hospitalize(this: IPlayer): number {
SnackbarEvents.emit(`You've been Hospitalized for ${numeralWrapper.formatMoney(cost)}`, "warning"); SnackbarEvents.emit(`You've been Hospitalized for ${numeralWrapper.formatMoney(cost)}`, "warning");
} }
this.loseMoney(cost); this.loseMoney(cost, "hospitalization");
this.recordMoneySource(-1 * cost, "hospitalization");
this.hp = this.max_hp; this.hp = this.max_hp;
return cost; return cost;
} }
@ -2601,8 +2596,7 @@ export function gainCodingContractReward(this: IPlayer, reward: ICodingContractR
case CodingContractRewardType.Money: case CodingContractRewardType.Money:
default: { default: {
const moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * BitNodeMultipliers.CodingContractMoney; const moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * BitNodeMultipliers.CodingContractMoney;
this.gainMoney(moneyGain); this.gainMoney(moneyGain, "codingcontract");
this.recordMoneySource(moneyGain, "codingcontract");
return `Gained ${numeralWrapper.formatMoney(moneyGain)}`; return `Gained ${numeralWrapper.formatMoney(moneyGain)}`;
} }
} }

@ -26,7 +26,7 @@ export function purchaseResleeve(r: Resleeve, p: IPlayer): boolean {
if (!p.canAfford(cost)) { if (!p.canAfford(cost)) {
return false; return false;
} }
p.loseMoney(cost); p.loseMoney(cost, "other");
// Set the player's exp // Set the player's exp
p.hacking_exp = r.hacking_exp; p.hacking_exp = r.hacking_exp;

@ -331,8 +331,7 @@ export class Sleeve extends Person {
const gain: number = task.money * numCycles; const gain: number = task.money * numCycles;
this.earningsForTask.money += gain; this.earningsForTask.money += gain;
this.earningsForPlayer.money += gain; this.earningsForPlayer.money += gain;
p.gainMoney(gain); p.gainMoney(gain, "sleeves");
p.recordMoneySource(gain, "sleeves");
} }
/** /**
@ -668,7 +667,7 @@ export class Sleeve extends Person {
* Travel to another City. Costs money from player * Travel to another City. Costs money from player
*/ */
travel(p: IPlayer, newCity: CityName): boolean { travel(p: IPlayer, newCity: CityName): boolean {
p.loseMoney(CONSTANTS.TravelCost); p.loseMoney(CONSTANTS.TravelCost, "sleeves");
this.city = newCity; this.city = newCity;
return true; return true;
@ -684,7 +683,7 @@ export class Sleeve extends Person {
return false; return false;
} }
p.loseMoney(aug.startingCost); p.loseMoney(aug.startingCost, "sleeves");
this.installAugmentation(aug); this.installAugmentation(aug);
return true; return true;
} }

@ -53,7 +53,7 @@ export function CovenantPurchasesRoot(props: IProps): React.ReactElement {
if (player.sleevesFromCovenant >= MaxSleevesFromCovenant) return; if (player.sleevesFromCovenant >= MaxSleevesFromCovenant) return;
if (player.canAfford(purchaseCost())) { if (player.canAfford(purchaseCost())) {
player.loseMoney(purchaseCost()); player.loseMoney(purchaseCost(), "sleeves");
player.sleevesFromCovenant += 1; player.sleevesFromCovenant += 1;
player.sleeves.push(new Sleeve(player)); player.sleeves.push(new Sleeve(player));
rerender(); rerender();

@ -53,7 +53,7 @@ export function CovenantSleeveMemoryUpgrade(props: IProps): React.ReactElement {
const cost = getPurchaseCost(); const cost = getPurchaseCost();
if (props.p.canAfford(cost)) { if (props.p.canAfford(cost)) {
props.sleeve.upgradeMemory(amt); props.sleeve.upgradeMemory(amt);
props.p.loseMoney(cost); props.p.loseMoney(cost, "sleeves");
props.rerender(); props.rerender();
} }
} }

@ -25,7 +25,7 @@ export function TravelModal(props: IProps): React.ReactElement {
dialogBoxCreate("You cannot afford to have this sleeve travel to another city"); dialogBoxCreate("You cannot afford to have this sleeve travel to another city");
} }
props.sleeve.city = city as CityName; props.sleeve.city = city as CityName;
player.loseMoney(CONSTANTS.TravelCost); player.loseMoney(CONSTANTS.TravelCost, "sleeve");
props.sleeve.resetTaskStatus(); props.sleeve.resetTaskStatus();
props.rerender(); props.rerender();
props.onClose(); props.onClose();

@ -94,7 +94,7 @@ export function purchaseServer(hostname: string, ram: number, cost: number, p: I
homeComputer.serversOnNetwork.push(newServ.hostname); homeComputer.serversOnNetwork.push(newServ.hostname);
newServ.serversOnNetwork.push(homeComputer.hostname); newServ.serversOnNetwork.push(homeComputer.hostname);
p.loseMoney(cost); p.loseMoney(cost, "servers");
dialogBoxCreate("Server successfully purchased with hostname " + hostname); dialogBoxCreate("Server successfully purchased with hostname " + hostname);
} }
@ -114,5 +114,5 @@ export function purchaseRamForHomeComputer(p: IPlayer): void {
} }
homeComputer.maxRam *= 2; homeComputer.maxRam *= 2;
p.loseMoney(cost); p.loseMoney(cost, "servers");
} }

@ -102,7 +102,7 @@ export function buyStock(
} }
const origTotal = stock.playerShares * stock.playerAvgPx; const origTotal = stock.playerShares * stock.playerAvgPx;
Player.loseMoney(totalPrice); Player.loseMoney(totalPrice, "stock");
const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission; const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission;
stock.playerShares = Math.round(stock.playerShares + shares); stock.playerShares = Math.round(stock.playerShares + shares);
stock.playerAvgPx = newTotal / stock.playerShares; stock.playerAvgPx = newTotal / stock.playerShares;
@ -171,8 +171,7 @@ export function sellStock(
if (isNaN(netProfit)) { if (isNaN(netProfit)) {
netProfit = 0; netProfit = 0;
} }
Player.gainMoney(gains); Player.gainMoney(gains, "stock");
Player.recordMoneySource(netProfit, "stock");
if (workerScript) { if (workerScript) {
workerScript.scriptRef.onlineMoneyMade += netProfit; workerScript.scriptRef.onlineMoneyMade += netProfit;
Player.scriptProdSinceLastAug += netProfit; Player.scriptProdSinceLastAug += netProfit;
@ -280,7 +279,7 @@ export function shortStock(
} }
const origTotal = stock.playerShortShares * stock.playerAvgShortPx; const origTotal = stock.playerShortShares * stock.playerAvgShortPx;
Player.loseMoney(totalPrice); Player.loseMoney(totalPrice, "stock");
const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission; const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission;
stock.playerShortShares = Math.round(stock.playerShortShares + shares); stock.playerShortShares = Math.round(stock.playerShortShares + shares);
stock.playerAvgShortPx = newTotal / stock.playerShortShares; stock.playerAvgShortPx = newTotal / stock.playerShortShares;
@ -364,8 +363,7 @@ export function sellShort(
if (isNaN(profit)) { if (isNaN(profit)) {
profit = 0; profit = 0;
} }
Player.gainMoney(totalGain); Player.gainMoney(totalGain, "stock");
Player.recordMoneySource(profit, "stock");
if (workerScript) { if (workerScript) {
workerScript.scriptRef.onlineMoneyMade += profit; workerScript.scriptRef.onlineMoneyMade += profit;
Player.scriptProdSinceLastAug += profit; Player.scriptProdSinceLastAug += profit;

@ -35,7 +35,7 @@ function Purchase4SMarketDataTixApiAccessButton(props: IProps): React.ReactEleme
return; return;
} }
props.p.has4SDataTixApi = true; props.p.has4SDataTixApi = true;
props.p.loseMoney(getStockMarket4STixApiCost()); props.p.loseMoney(getStockMarket4STixApiCost(), "stock");
props.rerender(); props.rerender();
} }
@ -88,7 +88,7 @@ function PurchaseWseAccountButton(props: IProps): React.ReactElement {
} }
props.p.hasWseAccount = true; props.p.hasWseAccount = true;
props.initStockMarket(); props.initStockMarket();
props.p.loseMoney(CONSTANTS.WSEAccountCost); props.p.loseMoney(CONSTANTS.WSEAccountCost, "stock");
props.rerender(); props.rerender();
} }
@ -113,7 +113,7 @@ function PurchaseTixApiAccessButton(props: IProps): React.ReactElement {
return; return;
} }
props.p.hasTixApiAccess = true; props.p.hasTixApiAccess = true;
props.p.loseMoney(CONSTANTS.TIXAPICost); props.p.loseMoney(CONSTANTS.TIXAPICost, "stock");
props.rerender(); props.rerender();
} }
@ -143,7 +143,7 @@ function Purchase4SMarketDataButton(props: IProps): React.ReactElement {
return; return;
} }
props.p.has4SData = true; props.p.has4SData = true;
props.p.loseMoney(getStockMarket4SDataCost()); props.p.loseMoney(getStockMarket4SDataCost(), "stock");
props.rerender(); props.rerender();
} }
if (props.p.has4SData) { if (props.p.has4SData) {

@ -196,8 +196,7 @@ export class Terminal implements ITerminal {
} // Safety check } // Safety check
server.moneyAvailable -= moneyGained; server.moneyAvailable -= moneyGained;
player.gainMoney(moneyGained); player.gainMoney(moneyGained, "hacking");
player.recordMoneySource(moneyGained, "hacking");
player.gainHackingExp(expGainedOnSuccess); player.gainHackingExp(expGainedOnSuccess);
player.gainIntelligenceExp(expGainedOnSuccess / CONSTANTS.IntelligenceTerminalHackBaseExpGain); player.gainIntelligenceExp(expGainedOnSuccess / CONSTANTS.IntelligenceTerminalHackBaseExpGain);

@ -253,7 +253,7 @@ const Engine: {
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); Player.gainMoney(offlineHackingIncome, "hacknet");
// Process offline progress // Process offline progress
loadAllRunningScripts(); // This also takes care of offline production for those scripts loadAllRunningScripts(); // This also takes care of offline production for those scripts
if (Player.isWorking) { if (Player.isWorking) {

@ -167,9 +167,15 @@ function MoneyModal({ open, onClose }: IMoneyModalProps): React.ReactElement {
const player = use.Player(); const player = use.Player();
function convertMoneySourceTrackerToString(src: MoneySourceTracker): React.ReactElement { function convertMoneySourceTrackerToString(src: MoneySourceTracker): React.ReactElement {
const parts: any[][] = [[`Total:`, <Money money={src.total} />]]; const parts: any[][] = [[`Total:`, <Money money={src.total} />]];
if (src.augmentations) {
parts.push([`Augmentations:`, <Money money={src.augmentations} />]);
}
if (src.bladeburner) { if (src.bladeburner) {
parts.push([`Bladeburner:`, <Money money={src.bladeburner} />]); parts.push([`Bladeburner:`, <Money money={src.bladeburner} />]);
} }
if (src.casino) {
parts.push([`Casino:`, <Money money={src.casino} />]);
}
if (src.codingcontract) { if (src.codingcontract) {
parts.push([`Coding Contracts:`, <Money money={src.codingcontract} />]); parts.push([`Coding Contracts:`, <Money money={src.codingcontract} />]);
} }
@ -191,8 +197,8 @@ function MoneyModal({ open, onClose }: IMoneyModalProps): React.ReactElement {
if (src.hacking) { if (src.hacking) {
parts.push([`Hacking:`, <Money money={src.hacking} />]); parts.push([`Hacking:`, <Money money={src.hacking} />]);
} }
if (src.hacknetnode) { if (src.hacknet) {
parts.push([`Hacknet Nodes:`, <Money money={src.hacknetnode} />]); parts.push([`Hacknet Nodes:`, <Money money={src.hacknet} />]);
} }
if (src.hospitalization) { if (src.hospitalization) {
parts.push([`Hospitalization:`, <Money money={src.hospitalization} />]); parts.push([`Hospitalization:`, <Money money={src.hospitalization} />]);
@ -200,15 +206,18 @@ function MoneyModal({ open, onClose }: IMoneyModalProps): React.ReactElement {
if (src.infiltration) { if (src.infiltration) {
parts.push([`Infiltration:`, <Money money={src.infiltration} />]); parts.push([`Infiltration:`, <Money money={src.infiltration} />]);
} }
if (src.servers) {
parts.push([`Servers:`, <Money money={src.servers} />]);
}
if (src.stock) { if (src.stock) {
parts.push([`Stock Market:`, <Money money={src.stock} />]); parts.push([`Stock Market:`, <Money money={src.stock} />]);
} }
if (src.casino) {
parts.push([`Casino:`, <Money money={src.casino} />]);
}
if (src.sleeves) { if (src.sleeves) {
parts.push([`Sleeves:`, <Money money={src.sleeves} />]); parts.push([`Sleeves:`, <Money money={src.sleeves} />]);
} }
if (src.other) {
parts.push([`Other:`, <Money money={src.other} />]);
}
return <StatsTable rows={parts} wide />; return <StatsTable rows={parts} wide />;
} }

@ -16,13 +16,16 @@ export class MoneySourceTracker {
crime = 0; crime = 0;
gang = 0; gang = 0;
hacking = 0; hacking = 0;
hacknetnode = 0; hacknet = 0;
hospitalization = 0; hospitalization = 0;
infiltration = 0; infiltration = 0;
sleeves = 0; sleeves = 0;
stock = 0; stock = 0;
total = 0; total = 0;
work = 0; work = 0;
servers = 0;
other = 0;
augmentations = 0;
// Record money earned // Record money earned
record(amt: number, source: string): void { record(amt: number, source: string): void {