mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-24 07:02:26 +01:00
convert taking class ot new work system
This commit is contained in:
parent
24b6eb4d56
commit
647392626e
@ -24,7 +24,7 @@ import { IMap } from "../types";
|
||||
import * as data from "./AchievementData.json";
|
||||
import { FactionNames } from "../Faction/data/FactionNames";
|
||||
import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames";
|
||||
import { ClassType } from "../utils/WorkType";
|
||||
import { isClassWork } from "../Work/ClassWork";
|
||||
|
||||
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
|
||||
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
|
||||
@ -391,10 +391,7 @@ export const achievements: IMap<Achievement> = {
|
||||
WORKOUT: {
|
||||
...achievementData["WORKOUT"],
|
||||
Icon: "WORKOUT",
|
||||
Condition: () =>
|
||||
[ClassType.GymStrength, ClassType.GymDefense, ClassType.GymDexterity, ClassType.GymAgility].includes(
|
||||
Player.className,
|
||||
),
|
||||
Condition: () => isClassWork(Player.currentWork),
|
||||
},
|
||||
TOR: {
|
||||
...achievementData["TOR"],
|
||||
|
@ -8,16 +8,13 @@ import Button from "@mui/material/Button";
|
||||
|
||||
import { Location } from "../Location";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { GetServer } from "../../Server/AllServers";
|
||||
import { Server } from "../../Server/Server";
|
||||
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { serverMetadata } from "../../Server/data/servers";
|
||||
import { Box } from "@mui/material";
|
||||
import { ClassType } from "../../utils/WorkType";
|
||||
import { ClassWork, ClassType, Classes } from "../../Work/ClassWork";
|
||||
import { calculateCost } from "../../Work/formulas/Class";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
@ -26,51 +23,32 @@ type IProps = {
|
||||
};
|
||||
|
||||
export function GymLocation(props: IProps): React.ReactElement {
|
||||
function calculateCost(): number {
|
||||
const serverMeta = serverMetadata.find((s) => s.specialName === props.loc.name);
|
||||
const server = GetServer(serverMeta ? serverMeta.hostname : "");
|
||||
if (server == null || !server.hasOwnProperty("backdoorInstalled")) return props.loc.costMult;
|
||||
const discount = (server as Server).backdoorInstalled ? 0.9 : 1;
|
||||
return props.loc.costMult * discount;
|
||||
}
|
||||
|
||||
function train(stat: ClassType): void {
|
||||
const loc = props.loc;
|
||||
props.p.startClass(calculateCost(), loc.expMult, stat);
|
||||
props.p.startNEWWork(
|
||||
new ClassWork({
|
||||
classType: stat,
|
||||
location: props.loc.name,
|
||||
singularity: false,
|
||||
}),
|
||||
);
|
||||
props.p.startFocusing();
|
||||
props.router.toWork();
|
||||
}
|
||||
|
||||
function trainStrength(): void {
|
||||
train(ClassType.GymStrength);
|
||||
}
|
||||
|
||||
function trainDefense(): void {
|
||||
train(ClassType.GymDefense);
|
||||
}
|
||||
|
||||
function trainDexterity(): void {
|
||||
train(ClassType.GymDexterity);
|
||||
}
|
||||
|
||||
function trainAgility(): void {
|
||||
train(ClassType.GymAgility);
|
||||
}
|
||||
|
||||
const cost = CONSTANTS.ClassGymBaseCost * calculateCost();
|
||||
const cost = calculateCost(Classes[ClassType.GymStrength], props.loc);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: "grid", width: "fit-content" }}>
|
||||
<Button onClick={trainStrength}>
|
||||
<Button onClick={() => train(ClassType.GymStrength)}>
|
||||
Train Strength (<Money money={cost} player={props.p} /> / sec)
|
||||
</Button>
|
||||
<Button onClick={trainDefense}>
|
||||
<Button onClick={() => train(ClassType.GymDefense)}>
|
||||
Train Defense (<Money money={cost} player={props.p} /> / sec)
|
||||
</Button>
|
||||
<Button onClick={trainDexterity}>
|
||||
<Button onClick={() => train(ClassType.GymDexterity)}>
|
||||
Train Dexterity (<Money money={cost} player={props.p} /> / sec)
|
||||
</Button>
|
||||
<Button onClick={trainAgility}>
|
||||
<Button onClick={() => train(ClassType.GymAgility)}>
|
||||
Train Agility (<Money money={cost} player={props.p} /> / sec)
|
||||
</Button>
|
||||
</Box>
|
||||
|
@ -9,15 +9,12 @@ import Button from "@mui/material/Button";
|
||||
|
||||
import { Location } from "../Location";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { GetServer } from "../../Server/AllServers";
|
||||
import { Server } from "../../Server/Server";
|
||||
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { use } from "../../ui/Context";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
import { ClassType } from "../../utils/WorkType";
|
||||
import { ClassWork, ClassType, Classes } from "../../Work/ClassWork";
|
||||
import { calculateCost } from "../../Work/formulas/Class";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
@ -27,51 +24,23 @@ export function UniversityLocation(props: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const router = use.Router();
|
||||
|
||||
function calculateCost(): number {
|
||||
const server = GetServer(props.loc.name);
|
||||
if (server == null || !server.hasOwnProperty("backdoorInstalled")) return props.loc.costMult;
|
||||
const discount = (server as Server).backdoorInstalled ? 0.9 : 1;
|
||||
return props.loc.costMult * discount;
|
||||
}
|
||||
|
||||
function take(stat: ClassType): void {
|
||||
const loc = props.loc;
|
||||
player.startClass(calculateCost(), loc.expMult, stat);
|
||||
function take(classType: ClassType): void {
|
||||
player.startNEWWork(
|
||||
new ClassWork({
|
||||
classType: classType,
|
||||
location: props.loc.name,
|
||||
singularity: false,
|
||||
}),
|
||||
);
|
||||
player.startFocusing();
|
||||
router.toWork();
|
||||
}
|
||||
|
||||
function study(): void {
|
||||
take(ClassType.StudyComputerScience);
|
||||
}
|
||||
|
||||
function dataStructures(): void {
|
||||
take(ClassType.DataStructures);
|
||||
}
|
||||
|
||||
function networks(): void {
|
||||
take(ClassType.Networks);
|
||||
}
|
||||
|
||||
function algorithms(): void {
|
||||
take(ClassType.Algorithms);
|
||||
}
|
||||
|
||||
function management(): void {
|
||||
take(ClassType.Management);
|
||||
}
|
||||
|
||||
function leadership(): void {
|
||||
take(ClassType.Leadership);
|
||||
}
|
||||
|
||||
const costMult: number = calculateCost();
|
||||
|
||||
const dataStructuresCost = CONSTANTS.ClassDataStructuresBaseCost * costMult;
|
||||
const networksCost = CONSTANTS.ClassNetworksBaseCost * costMult;
|
||||
const algorithmsCost = CONSTANTS.ClassAlgorithmsBaseCost * costMult;
|
||||
const managementCost = CONSTANTS.ClassManagementBaseCost * costMult;
|
||||
const leadershipCost = CONSTANTS.ClassLeadershipBaseCost * costMult;
|
||||
const dataStructuresCost = calculateCost(Classes[ClassType.DataStructures], props.loc);
|
||||
const networksCost = calculateCost(Classes[ClassType.Networks], props.loc);
|
||||
const algorithmsCost = calculateCost(Classes[ClassType.Algorithms], props.loc);
|
||||
const managementCost = calculateCost(Classes[ClassType.Management], props.loc);
|
||||
const leadershipCost = calculateCost(Classes[ClassType.Leadership], props.loc);
|
||||
|
||||
const earnHackingExpTooltip = `Gain hacking experience!`;
|
||||
const earnCharismaExpTooltip = `Gain charisma experience!`;
|
||||
@ -79,34 +48,34 @@ export function UniversityLocation(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<Box sx={{ display: "grid", width: "fit-content" }}>
|
||||
<Tooltip title={earnHackingExpTooltip}>
|
||||
<Button onClick={study}>Study Computer Science (free)</Button>
|
||||
<Button onClick={() => take(ClassType.StudyComputerScience)}>Study Computer Science (free)</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={earnHackingExpTooltip}>
|
||||
<Button onClick={dataStructures}>
|
||||
<Button onClick={() => take(ClassType.DataStructures)}>
|
||||
Take Data Structures course (
|
||||
<Money money={dataStructuresCost} player={player} /> / sec)
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={earnHackingExpTooltip}>
|
||||
<Button onClick={networks}>
|
||||
<Button onClick={() => take(ClassType.Networks)}>
|
||||
Take Networks course (
|
||||
<Money money={networksCost} player={player} /> / sec)
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={earnHackingExpTooltip}>
|
||||
<Button onClick={algorithms}>
|
||||
<Button onClick={() => take(ClassType.Algorithms)}>
|
||||
Take Algorithms course (
|
||||
<Money money={algorithmsCost} player={player} /> / sec)
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={earnCharismaExpTooltip}>
|
||||
<Button onClick={management}>
|
||||
<Button onClick={() => take(ClassType.Management)}>
|
||||
Take Management course (
|
||||
<Money money={managementCost} player={player} /> / sec)
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={earnCharismaExpTooltip}>
|
||||
<Button onClick={leadership}>
|
||||
<Button onClick={() => take(ClassType.Leadership)}>
|
||||
Take Leadership course (
|
||||
<Money money={leadershipCost} player={player} /> / sec)
|
||||
</Button>
|
||||
|
@ -2420,7 +2420,6 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
workMoneyGained: Player.workMoneyGained,
|
||||
createProgramName: Player.createProgramName,
|
||||
createProgramReqLvl: Player.createProgramReqLvl,
|
||||
className: Player.className,
|
||||
work_money_mult: Player.work_money_mult,
|
||||
hacknet_node_money_mult: Player.hacknet_node_money_mult,
|
||||
hacknet_node_purchase_cost_mult: Player.hacknet_node_purchase_cost_mult,
|
||||
|
@ -49,7 +49,8 @@ import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
|
||||
import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames";
|
||||
import { enterBitNode } from "../RedPill";
|
||||
import { FactionNames } from "../Faction/data/FactionNames";
|
||||
import { ClassType, WorkType } from "../utils/WorkType";
|
||||
import { WorkType } from "../utils/WorkType";
|
||||
import { ClassWork, ClassType } from "../Work/ClassWork";
|
||||
|
||||
export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript): InternalAPI<ISingularity> {
|
||||
const getAugmentation = function (_ctx: NetscriptContext, name: string): Augmentation {
|
||||
@ -264,12 +265,11 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
const className = _ctx.helper.string("className", _className);
|
||||
const focus = _ctx.helper.boolean(_focus);
|
||||
const wasFocusing = player.focus;
|
||||
if (player.isWorking) {
|
||||
if (player.currentWork) {
|
||||
const txt = player.singularityStopWork();
|
||||
_ctx.log(() => txt);
|
||||
}
|
||||
|
||||
let costMult, expMult;
|
||||
switch (universityName.toLowerCase()) {
|
||||
case LocationName.AevumSummitUniversity.toLowerCase():
|
||||
if (player.city != CityName.Aevum) {
|
||||
@ -277,8 +277,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
return false;
|
||||
}
|
||||
player.gotoLocation(LocationName.AevumSummitUniversity);
|
||||
costMult = 4;
|
||||
expMult = 3;
|
||||
break;
|
||||
case LocationName.Sector12RothmanUniversity.toLowerCase():
|
||||
if (player.city != CityName.Sector12) {
|
||||
@ -286,8 +284,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
return false;
|
||||
}
|
||||
player.location = LocationName.Sector12RothmanUniversity;
|
||||
costMult = 3;
|
||||
expMult = 2;
|
||||
break;
|
||||
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase():
|
||||
if (player.city != CityName.Volhaven) {
|
||||
@ -297,8 +293,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
return false;
|
||||
}
|
||||
player.location = LocationName.VolhavenZBInstituteOfTechnology;
|
||||
costMult = 5;
|
||||
expMult = 4;
|
||||
break;
|
||||
default:
|
||||
_ctx.log(() => `Invalid university name: '${universityName}'.`);
|
||||
@ -329,7 +323,13 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
_ctx.log(() => `Invalid class name: ${className}.`);
|
||||
return false;
|
||||
}
|
||||
player.startClass(costMult, expMult, task);
|
||||
player.startNEWWork(
|
||||
new ClassWork({
|
||||
classType: task,
|
||||
location: player.location,
|
||||
singularity: true,
|
||||
}),
|
||||
);
|
||||
if (focus) {
|
||||
player.startFocusing();
|
||||
Router.toWork();
|
||||
@ -352,7 +352,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
const txt = player.singularityStopWork();
|
||||
_ctx.log(() => txt);
|
||||
}
|
||||
let costMult, expMult;
|
||||
switch (gymName.toLowerCase()) {
|
||||
case LocationName.AevumCrushFitnessGym.toLowerCase():
|
||||
if (player.city != CityName.Aevum) {
|
||||
@ -363,8 +362,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
return false;
|
||||
}
|
||||
player.location = LocationName.AevumCrushFitnessGym;
|
||||
costMult = 3;
|
||||
expMult = 2;
|
||||
break;
|
||||
case LocationName.AevumSnapFitnessGym.toLowerCase():
|
||||
if (player.city != CityName.Aevum) {
|
||||
@ -375,8 +372,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
return false;
|
||||
}
|
||||
player.location = LocationName.AevumSnapFitnessGym;
|
||||
costMult = 10;
|
||||
expMult = 5;
|
||||
break;
|
||||
case LocationName.Sector12IronGym.toLowerCase():
|
||||
if (player.city != CityName.Sector12) {
|
||||
@ -387,8 +382,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
return false;
|
||||
}
|
||||
player.location = LocationName.Sector12IronGym;
|
||||
costMult = 1;
|
||||
expMult = 1;
|
||||
break;
|
||||
case LocationName.Sector12PowerhouseGym.toLowerCase():
|
||||
if (player.city != CityName.Sector12) {
|
||||
@ -399,8 +392,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
return false;
|
||||
}
|
||||
player.location = LocationName.Sector12PowerhouseGym;
|
||||
costMult = 20;
|
||||
expMult = 10;
|
||||
break;
|
||||
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase():
|
||||
if (player.city != CityName.Volhaven) {
|
||||
@ -411,8 +402,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
return false;
|
||||
}
|
||||
player.location = LocationName.VolhavenMilleniumFitnessGym;
|
||||
costMult = 7;
|
||||
expMult = 4;
|
||||
break;
|
||||
default:
|
||||
_ctx.log(() => `Invalid gym name: ${gymName}. gymWorkout() failed`);
|
||||
@ -422,19 +411,27 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
switch (stat.toLowerCase()) {
|
||||
case "strength".toLowerCase():
|
||||
case "str".toLowerCase():
|
||||
player.startClass(costMult, expMult, ClassType.GymStrength);
|
||||
player.startNEWWork(
|
||||
new ClassWork({ classType: ClassType.GymStrength, location: player.location, singularity: true }),
|
||||
);
|
||||
break;
|
||||
case "defense".toLowerCase():
|
||||
case "def".toLowerCase():
|
||||
player.startClass(costMult, expMult, ClassType.GymDefense);
|
||||
player.startNEWWork(
|
||||
new ClassWork({ classType: ClassType.GymDefense, location: player.location, singularity: true }),
|
||||
);
|
||||
break;
|
||||
case "dexterity".toLowerCase():
|
||||
case "dex".toLowerCase():
|
||||
player.startClass(costMult, expMult, ClassType.GymDexterity);
|
||||
player.startNEWWork(
|
||||
new ClassWork({ classType: ClassType.GymDexterity, location: player.location, singularity: true }),
|
||||
);
|
||||
break;
|
||||
case "agility".toLowerCase():
|
||||
case "agi".toLowerCase():
|
||||
player.startClass(costMult, expMult, ClassType.GymAgility);
|
||||
player.startNEWWork(
|
||||
new ClassWork({ classType: ClassType.GymAgility, location: player.location, singularity: true }),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
_ctx.log(() => `Invalid stat: ${stat}.`);
|
||||
|
@ -134,7 +134,6 @@ export interface IPlayer extends IPerson {
|
||||
timeWorkedGraftAugmentation: number;
|
||||
timeNeededToCompleteWork: number;
|
||||
focus: boolean;
|
||||
className: ClassType;
|
||||
currentWorkFactionName: string;
|
||||
workType: WorkType;
|
||||
workCostMult: number;
|
||||
@ -213,7 +212,6 @@ export interface IPlayer extends IPerson {
|
||||
singularityStopWork(): string;
|
||||
startBladeburner(p: any): void;
|
||||
startFactionWork(faction: Faction): void;
|
||||
startClass(costMult: number, expMult: number, className: ClassType): void;
|
||||
startCorporation(corpName: string, additionalShares?: number): void;
|
||||
startFactionFieldWork(faction: Faction): void;
|
||||
startFactionHackWork(faction: Faction): void;
|
||||
@ -236,7 +234,6 @@ export interface IPlayer extends IPerson {
|
||||
gainCodingContractReward(reward: ICodingContractReward, difficulty?: number): string;
|
||||
stopFocusing(): void;
|
||||
finishFactionWork(cancelled: boolean, sing?: boolean): string;
|
||||
finishClass(sing?: boolean): string;
|
||||
finishWork(cancelled: boolean, sing?: boolean): string;
|
||||
cancelationPenalty(): number;
|
||||
finishWorkPartTime(sing?: boolean): string;
|
||||
@ -257,7 +254,6 @@ export interface IPlayer extends IPerson {
|
||||
processWorkEarnings(cycles: number): void;
|
||||
hospitalize(): void;
|
||||
createProgramWork(numCycles: number): boolean;
|
||||
takeClass(numCycles: number): boolean;
|
||||
checkForFactionInvitations(): Faction[];
|
||||
setBitNodeNumber(n: number): void;
|
||||
getMult(name: string): number;
|
||||
|
@ -10,7 +10,6 @@ import { IMap } from "../../types";
|
||||
import { Sleeve } from "../Sleeve/Sleeve";
|
||||
import { IPlayerOwnedSourceFile } from "../../SourceFile/PlayerOwnedSourceFile";
|
||||
import { Exploit } from "../../Exploits/Exploit";
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
import { CompanyPosition } from "../../Company/CompanyPosition";
|
||||
import { Server } from "../../Server/Server";
|
||||
import { BaseServer } from "../../Server/BaseServer";
|
||||
@ -147,7 +146,6 @@ export class PlayerObject implements IPlayer {
|
||||
timeWorkedGraftAugmentation: number;
|
||||
timeNeededToCompleteWork: number;
|
||||
focus: boolean;
|
||||
className: ClassType;
|
||||
currentWorkFactionName: string;
|
||||
workType: WorkType;
|
||||
workCostMult: number;
|
||||
@ -235,7 +233,6 @@ export class PlayerObject implements IPlayer {
|
||||
singularityStopWork: () => string;
|
||||
startBladeburner: (p: any) => void;
|
||||
startFactionWork: (faction: Faction) => void;
|
||||
startClass: (costMult: number, expMult: number, className: ClassType) => void;
|
||||
startCorporation: (corpName: string, additionalShares?: number) => void;
|
||||
startFactionFieldWork: (faction: Faction) => void;
|
||||
startFactionHackWork: (faction: Faction) => void;
|
||||
@ -262,7 +259,6 @@ export class PlayerObject implements IPlayer {
|
||||
gainCodingContractReward: (reward: ICodingContractReward, difficulty?: number) => string;
|
||||
stopFocusing: () => void;
|
||||
finishFactionWork: (cancelled: boolean, sing?: boolean) => string;
|
||||
finishClass: (sing?: boolean) => string;
|
||||
finishWork: (cancelled: boolean, sing?: boolean) => string;
|
||||
cancelationPenalty: () => number;
|
||||
finishWorkPartTime: (sing?: boolean) => string;
|
||||
@ -284,7 +280,6 @@ export class PlayerObject implements IPlayer {
|
||||
processWorkEarnings: (cycles: number) => void;
|
||||
hospitalize: () => void;
|
||||
createProgramWork: (numCycles: number) => boolean;
|
||||
takeClass: (numCycles: number) => boolean;
|
||||
checkForFactionInvitations: () => Faction[];
|
||||
setBitNodeNumber: (n: number) => void;
|
||||
getMult: (name: string) => number;
|
||||
@ -421,8 +416,6 @@ export class PlayerObject implements IPlayer {
|
||||
this.graftAugmentationName = "";
|
||||
this.timeWorkedGraftAugmentation = 0;
|
||||
|
||||
this.className = ClassType.None;
|
||||
|
||||
this.timeWorked = 0; //in m;
|
||||
this.timeWorkedCreateProgram = 0;
|
||||
this.timeNeededToCompleteWork = 0;
|
||||
@ -551,9 +544,6 @@ export class PlayerObject implements IPlayer {
|
||||
this.startGraftAugmentationWork = generalMethods.startGraftAugmentationWork;
|
||||
this.graftAugmentationWork = generalMethods.craftAugmentationWork;
|
||||
this.finishGraftAugmentationWork = generalMethods.finishGraftAugmentationWork;
|
||||
this.startClass = generalMethods.startClass;
|
||||
this.takeClass = generalMethods.takeClass;
|
||||
this.finishClass = generalMethods.finishClass;
|
||||
this.singularityStopWork = generalMethods.singularityStopWork;
|
||||
this.takeDamage = generalMethods.takeDamage;
|
||||
this.regenerateHp = generalMethods.regenerateHp;
|
||||
|
@ -61,7 +61,6 @@ import { Money } from "../../ui/React/Money";
|
||||
import React from "react";
|
||||
import { serverMetadata } from "../../Server/data/servers";
|
||||
import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar";
|
||||
import { calculateClassEarnings } from "../formulas/work";
|
||||
import { achievements } from "../../Achievements/Achievements";
|
||||
import { FactionNames } from "../../Faction/data/FactionNames";
|
||||
import { ITaskTracker } from "../ITaskTracker";
|
||||
@ -145,7 +144,6 @@ export function prestigeAugmentation(this: PlayerObject): void {
|
||||
this.currentWorkFactionName = "";
|
||||
this.currentWorkFactionDescription = "";
|
||||
this.createProgramName = "";
|
||||
this.className = ClassType.None;
|
||||
|
||||
this.workHackExpGainRate = 0;
|
||||
this.workStrExpGainRate = 0;
|
||||
@ -546,7 +544,6 @@ export function resetWorkStatus(this: IPlayer, generalType?: WorkType, group?: s
|
||||
this.currentWorkFactionDescription = "";
|
||||
this.createProgramName = "";
|
||||
this.graftAugmentationName = "";
|
||||
this.className = ClassType.None;
|
||||
this.workType = WorkType.None;
|
||||
}
|
||||
|
||||
@ -568,7 +565,7 @@ export function processWorkEarnings(this: IPlayer, numCycles = 1): void {
|
||||
this.gainDexterityExp(dexExpGain);
|
||||
this.gainAgilityExp(agiExpGain);
|
||||
this.gainCharismaExp(chaExpGain);
|
||||
this.gainMoney(moneyGain, this.className ? "class" : "work");
|
||||
this.gainMoney(moneyGain, "work");
|
||||
this.workHackExpGained += hackExpGain;
|
||||
this.workStrExpGained += strExpGain;
|
||||
this.workDefExpGained += defExpGain;
|
||||
@ -610,10 +607,6 @@ export function process(this: IPlayer, router: IRouter, numCycles = 1): void {
|
||||
if (this.createProgramWork(numCycles)) {
|
||||
router.toTerminal();
|
||||
}
|
||||
} else if (this.workType === WorkType.StudyClass) {
|
||||
if (this.takeClass(numCycles)) {
|
||||
router.toCity();
|
||||
}
|
||||
} else if (this.workType === WorkType.CompanyPartTime) {
|
||||
if (this.workPartTime(numCycles)) {
|
||||
router.toCity();
|
||||
@ -1401,109 +1394,17 @@ export function finishGraftAugmentationWork(this: IPlayer, cancelled: boolean, s
|
||||
return `Grafting of ${augName} has ended.`;
|
||||
}
|
||||
|
||||
/* Studying/Taking Classes */
|
||||
export function startClass(this: IPlayer, costMult: number, expMult: number, className: ClassType): void {
|
||||
this.resetWorkStatus();
|
||||
this.isWorking = true;
|
||||
this.workType = WorkType.StudyClass;
|
||||
this.workCostMult = costMult;
|
||||
this.workExpMult = expMult;
|
||||
this.className = className;
|
||||
const earnings = calculateClassEarnings(this);
|
||||
this.workMoneyLossRate = earnings.workMoneyLossRate;
|
||||
this.workHackExpGainRate = earnings.workHackExpGainRate;
|
||||
this.workStrExpGainRate = earnings.workStrExpGainRate;
|
||||
this.workDefExpGainRate = earnings.workDefExpGainRate;
|
||||
this.workDexExpGainRate = earnings.workDexExpGainRate;
|
||||
this.workAgiExpGainRate = earnings.workAgiExpGainRate;
|
||||
this.workChaExpGainRate = earnings.workChaExpGainRate;
|
||||
}
|
||||
|
||||
export function takeClass(this: IPlayer, numCycles: number): boolean {
|
||||
this.timeWorked += CONSTANTS._idleSpeed * numCycles;
|
||||
const earnings = calculateClassEarnings(this);
|
||||
this.workMoneyLossRate = earnings.workMoneyLossRate;
|
||||
this.workHackExpGainRate = earnings.workHackExpGainRate;
|
||||
this.workStrExpGainRate = earnings.workStrExpGainRate;
|
||||
this.workDefExpGainRate = earnings.workDefExpGainRate;
|
||||
this.workDexExpGainRate = earnings.workDexExpGainRate;
|
||||
this.workAgiExpGainRate = earnings.workAgiExpGainRate;
|
||||
this.workChaExpGainRate = earnings.workChaExpGainRate;
|
||||
this.processWorkEarnings(numCycles);
|
||||
return false;
|
||||
}
|
||||
|
||||
//The 'sing' argument defines whether or not this function was called
|
||||
//through a Singularity Netscript function
|
||||
export function finishClass(this: IPlayer, sing = false): string {
|
||||
this.gainIntelligenceExp(CONSTANTS.IntelligenceClassBaseExpGain * Math.round(this.timeWorked / 1000));
|
||||
|
||||
if (this.workMoneyGained > 0) {
|
||||
throw new Error("ERR: Somehow gained money while taking class");
|
||||
}
|
||||
|
||||
this.updateSkillLevels();
|
||||
if (!sing) {
|
||||
dialogBoxCreate(
|
||||
<>
|
||||
After {this.className} for {convertTimeMsToTimeElapsedString(this.timeWorked)}, <br />
|
||||
you spent a total of <Money money={-this.workMoneyGained} />. <br />
|
||||
<br />
|
||||
You earned a total of: <br />
|
||||
{numeralWrapper.formatExp(this.workHackExpGained)} hacking exp <br />
|
||||
{numeralWrapper.formatExp(this.workStrExpGained)} strength exp <br />
|
||||
{numeralWrapper.formatExp(this.workDefExpGained)} defense exp <br />
|
||||
{numeralWrapper.formatExp(this.workDexExpGained)} dexterity exp <br />
|
||||
{numeralWrapper.formatExp(this.workAgiExpGained)} agility exp <br />
|
||||
{numeralWrapper.formatExp(this.workChaExpGained)} charisma exp
|
||||
<br />
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
this.isWorking = false;
|
||||
|
||||
if (sing) {
|
||||
const res =
|
||||
"After " +
|
||||
this.className +
|
||||
" for " +
|
||||
convertTimeMsToTimeElapsedString(this.timeWorked) +
|
||||
", " +
|
||||
"you spent a total of " +
|
||||
numeralWrapper.formatMoney(this.workMoneyGained * -1) +
|
||||
". " +
|
||||
"You earned a total of: " +
|
||||
numeralWrapper.formatExp(this.workHackExpGained) +
|
||||
" hacking exp, " +
|
||||
numeralWrapper.formatExp(this.workStrExpGained) +
|
||||
" strength exp, " +
|
||||
numeralWrapper.formatExp(this.workDefExpGained) +
|
||||
" defense exp, " +
|
||||
numeralWrapper.formatExp(this.workDexExpGained) +
|
||||
" dexterity exp, " +
|
||||
numeralWrapper.formatExp(this.workAgiExpGained) +
|
||||
" agility exp, and " +
|
||||
numeralWrapper.formatExp(this.workChaExpGained) +
|
||||
" charisma exp";
|
||||
this.resetWorkStatus();
|
||||
return res;
|
||||
}
|
||||
this.resetWorkStatus();
|
||||
return "";
|
||||
}
|
||||
|
||||
//Cancels the player's current "work" assignment and gives the proper rewards
|
||||
//Used only for Singularity functions, so no popups are created
|
||||
export function singularityStopWork(this: IPlayer): string {
|
||||
if (this.currentWork !== null) {
|
||||
this.finishNEWWork(true);
|
||||
}
|
||||
if (!this.isWorking) {
|
||||
return "";
|
||||
}
|
||||
let res = ""; //Earnings text for work
|
||||
switch (this.workType) {
|
||||
case WorkType.StudyClass:
|
||||
res = this.finishClass(true);
|
||||
break;
|
||||
case WorkType.Company:
|
||||
res = this.finishWork(true, true);
|
||||
break;
|
||||
|
@ -1,81 +0,0 @@
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { IPlayer } from "../IPlayer";
|
||||
import { ClassType } from "../../utils/WorkType";
|
||||
|
||||
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 ClassType.StudyComputerScience:
|
||||
hackExp =
|
||||
((CONSTANTS.ClassStudyComputerScienceBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
|
||||
break;
|
||||
case ClassType.DataStructures:
|
||||
cost = (CONSTANTS.ClassDataStructuresBaseCost * player.workCostMult) / gameCPS;
|
||||
hackExp = ((CONSTANTS.ClassDataStructuresBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
|
||||
break;
|
||||
case ClassType.Networks:
|
||||
cost = (CONSTANTS.ClassNetworksBaseCost * player.workCostMult) / gameCPS;
|
||||
hackExp = ((CONSTANTS.ClassNetworksBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
|
||||
break;
|
||||
case ClassType.Algorithms:
|
||||
cost = (CONSTANTS.ClassAlgorithmsBaseCost * player.workCostMult) / gameCPS;
|
||||
hackExp = ((CONSTANTS.ClassAlgorithmsBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
|
||||
break;
|
||||
case ClassType.Management:
|
||||
cost = (CONSTANTS.ClassManagementBaseCost * player.workCostMult) / gameCPS;
|
||||
chaExp = ((CONSTANTS.ClassManagementBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
|
||||
break;
|
||||
case ClassType.Leadership:
|
||||
cost = (CONSTANTS.ClassLeadershipBaseCost * player.workCostMult) / gameCPS;
|
||||
chaExp = ((CONSTANTS.ClassLeadershipBaseExp * player.workExpMult) / gameCPS) * hashManager.getStudyMult();
|
||||
break;
|
||||
case ClassType.GymStrength:
|
||||
cost = (CONSTANTS.ClassGymBaseCost * player.workCostMult) / gameCPS;
|
||||
strExp = (player.workExpMult / gameCPS) * hashManager.getTrainingMult();
|
||||
break;
|
||||
case ClassType.GymDefense:
|
||||
cost = (CONSTANTS.ClassGymBaseCost * player.workCostMult) / gameCPS;
|
||||
defExp = (player.workExpMult / gameCPS) * hashManager.getTrainingMult();
|
||||
break;
|
||||
case ClassType.GymDexterity:
|
||||
cost = (CONSTANTS.ClassGymBaseCost * player.workCostMult) / gameCPS;
|
||||
dexExp = (player.workExpMult / gameCPS) * hashManager.getTrainingMult();
|
||||
break;
|
||||
case ClassType.GymAgility:
|
||||
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,
|
||||
};
|
||||
}
|
1
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
1
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -70,7 +70,6 @@ interface Player {
|
||||
workMoneyGained: number;
|
||||
createProgramName: string;
|
||||
createProgramReqLvl: number;
|
||||
className: string;
|
||||
work_money_mult: number;
|
||||
hacknet_node_money_mult: number;
|
||||
hacknet_node_purchase_cost_mult: number;
|
||||
|
@ -46,6 +46,7 @@ export function GetServer(s: string): BaseServer | null {
|
||||
const server = AllServers[s];
|
||||
if (server) return server;
|
||||
}
|
||||
console.log(AllServers);
|
||||
|
||||
if (!isValidIPAddress(s)) {
|
||||
return GetServerByHostname(s);
|
||||
|
204
src/Work/ClassWork.tsx
Normal file
204
src/Work/ClassWork.tsx
Normal file
@ -0,0 +1,204 @@
|
||||
import React from "react";
|
||||
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { LocationName } from "../Locations/data/LocationNames";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { Money } from "../ui/React/Money";
|
||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { calculateClassEarnings as calculateClassEarningsRate } from "./formulas/Class";
|
||||
import { Work, WorkType } from "./Work";
|
||||
import { applyWorkStats, newWorkStats, sumWorkStats, WorkStats } from "./WorkStats";
|
||||
|
||||
export enum ClassType {
|
||||
StudyComputerScience = "StudyComputerScience",
|
||||
DataStructures = "DataStructures",
|
||||
Networks = "Networks",
|
||||
Algorithms = "Algorithms",
|
||||
|
||||
Management = "Management",
|
||||
Leadership = "Leadership",
|
||||
|
||||
GymStrength = "GymStrength",
|
||||
GymDefense = "GymDefense",
|
||||
GymDexterity = "GymDexterity",
|
||||
GymAgility = "GymAgility",
|
||||
}
|
||||
|
||||
export interface Class {
|
||||
youAreCurrently: string;
|
||||
type: ClassType;
|
||||
earnings: WorkStats;
|
||||
}
|
||||
|
||||
export const Classes: Record<ClassType, Class> = {
|
||||
[ClassType.StudyComputerScience]: {
|
||||
youAreCurrently: "studying Computer Science",
|
||||
type: ClassType.StudyComputerScience,
|
||||
earnings: newWorkStats({ hackExp: 0.5, intExp: 0.01 }),
|
||||
},
|
||||
[ClassType.DataStructures]: {
|
||||
youAreCurrently: "taking a Data Structures course",
|
||||
type: ClassType.DataStructures,
|
||||
earnings: newWorkStats({
|
||||
money: -40,
|
||||
hackExp: 1,
|
||||
intExp: 0.01,
|
||||
}),
|
||||
},
|
||||
[ClassType.Networks]: {
|
||||
youAreCurrently: "taking a Networks course",
|
||||
type: ClassType.Networks,
|
||||
earnings: newWorkStats({
|
||||
money: -80,
|
||||
hackExp: 2,
|
||||
intExp: 0.01,
|
||||
}),
|
||||
},
|
||||
[ClassType.Algorithms]: {
|
||||
youAreCurrently: "taking an Algorithms course",
|
||||
type: ClassType.Algorithms,
|
||||
earnings: newWorkStats({
|
||||
money: -320,
|
||||
hackExp: 4,
|
||||
intExp: 0.01,
|
||||
}),
|
||||
},
|
||||
[ClassType.Management]: {
|
||||
youAreCurrently: "taking a Management course",
|
||||
type: ClassType.Management,
|
||||
earnings: newWorkStats({
|
||||
money: -160,
|
||||
chaExp: 2,
|
||||
intExp: 0.01,
|
||||
}),
|
||||
},
|
||||
[ClassType.Leadership]: {
|
||||
youAreCurrently: "taking a Leadership course",
|
||||
type: ClassType.Leadership,
|
||||
earnings: newWorkStats({
|
||||
money: -320,
|
||||
chaExp: 4,
|
||||
intExp: 0.01,
|
||||
}),
|
||||
},
|
||||
[ClassType.GymStrength]: {
|
||||
youAreCurrently: "training your strength at a gym",
|
||||
type: ClassType.GymStrength,
|
||||
earnings: newWorkStats({
|
||||
money: -120,
|
||||
strExp: 1,
|
||||
}),
|
||||
},
|
||||
[ClassType.GymDefense]: {
|
||||
youAreCurrently: "training your defense at a gym",
|
||||
type: ClassType.GymDefense,
|
||||
earnings: newWorkStats({
|
||||
money: -120,
|
||||
defExp: 1,
|
||||
}),
|
||||
},
|
||||
[ClassType.GymDexterity]: {
|
||||
youAreCurrently: "training your dexterity at a gym",
|
||||
type: ClassType.GymDexterity,
|
||||
earnings: newWorkStats({
|
||||
money: -120,
|
||||
dexExp: 1,
|
||||
}),
|
||||
},
|
||||
[ClassType.GymAgility]: {
|
||||
youAreCurrently: "training your agility at a gym",
|
||||
type: ClassType.GymAgility,
|
||||
earnings: newWorkStats({
|
||||
money: -120,
|
||||
agiExp: 1,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
interface ClassWorkParams {
|
||||
classType: ClassType;
|
||||
location: LocationName;
|
||||
singularity: boolean;
|
||||
}
|
||||
|
||||
export const isClassWork = (w: Work | null): w is ClassWork => w !== null && w.type === WorkType.CLASS;
|
||||
|
||||
export class ClassWork extends Work {
|
||||
type = WorkType.CLASS;
|
||||
|
||||
classType: ClassType;
|
||||
location: LocationName;
|
||||
cyclesWorked: number;
|
||||
singularity: boolean;
|
||||
earnings = newWorkStats();
|
||||
|
||||
constructor(params?: ClassWorkParams) {
|
||||
super();
|
||||
this.classType = params?.classType ?? ClassType.StudyComputerScience;
|
||||
this.location = params?.location ?? LocationName.Sector12RothmanUniversity;
|
||||
this.singularity = params?.singularity ?? false;
|
||||
this.cyclesWorked = 0;
|
||||
}
|
||||
|
||||
isGym(): boolean {
|
||||
return [ClassType.GymAgility, ClassType.GymDefense, ClassType.GymDexterity, ClassType.GymStrength].includes(
|
||||
this.classType,
|
||||
);
|
||||
}
|
||||
|
||||
getClass(): Class {
|
||||
return Classes[this.classType];
|
||||
}
|
||||
|
||||
calculateRates(player: IPlayer): WorkStats {
|
||||
return calculateClassEarningsRate(player, this);
|
||||
}
|
||||
|
||||
process(player: IPlayer, cycles: number): boolean {
|
||||
this.cyclesWorked += cycles;
|
||||
const rate = this.calculateRates(player);
|
||||
const earnings = applyWorkStats(player, rate, cycles, "class");
|
||||
this.earnings = sumWorkStats(this.earnings, earnings);
|
||||
return false;
|
||||
}
|
||||
|
||||
finish(): void {
|
||||
if (!this.singularity) {
|
||||
dialogBoxCreate(
|
||||
<>
|
||||
After {this.getClass().youAreCurrently} for{" "}
|
||||
{convertTimeMsToTimeElapsedString(this.cyclesWorked * CONSTANTS._idleSpeed)}, <br />
|
||||
you spent a total of <Money money={-this.earnings.money} />. <br />
|
||||
<br />
|
||||
You earned a total of: <br />
|
||||
{numeralWrapper.formatExp(this.earnings.hackExp)} hacking exp <br />
|
||||
{numeralWrapper.formatExp(this.earnings.strExp)} strength exp <br />
|
||||
{numeralWrapper.formatExp(this.earnings.defExp)} defense exp <br />
|
||||
{numeralWrapper.formatExp(this.earnings.dexExp)} dexterity exp <br />
|
||||
{numeralWrapper.formatExp(this.earnings.agiExp)} agility exp <br />
|
||||
{numeralWrapper.formatExp(this.earnings.chaExp)} charisma exp
|
||||
<br />
|
||||
</>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the current object to a JSON save state.
|
||||
*/
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("ClassWork", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiatizes a ClassWork object from a JSON save state.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
static fromJSON(value: any): ClassWork {
|
||||
return Generic_fromJSON(ClassWork, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.ClassWork = ClassWork;
|
@ -8,18 +8,18 @@ import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { Money } from "../ui/React/Money";
|
||||
import { CrimeType, WorkType } from "../utils/WorkType";
|
||||
import { Work } from "./Work";
|
||||
import { CrimeType } from "../utils/WorkType";
|
||||
import { Work, WorkType } from "./Work";
|
||||
|
||||
interface CrimeWorkParams {
|
||||
crimeType: CrimeType;
|
||||
singularity: boolean;
|
||||
}
|
||||
|
||||
export const isCrimeWork = (w: Work): w is CrimeWork => w.type === WorkType.Crime;
|
||||
export const isCrimeWork = (w: Work | null): w is CrimeWork => w !== null && w.type === WorkType.CRIME;
|
||||
|
||||
export class CrimeWork extends Work {
|
||||
type = WorkType.Crime;
|
||||
type = WorkType.CRIME;
|
||||
|
||||
crimeType: CrimeType;
|
||||
cyclesWorked: number;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { IPlayer } from "src/PersonObjects/IPlayer";
|
||||
import { WorkType } from "src/utils/WorkType";
|
||||
|
||||
export abstract class Work {
|
||||
abstract type: WorkType;
|
||||
@ -7,3 +6,8 @@ export abstract class Work {
|
||||
abstract process(player: IPlayer, cycles: number): boolean;
|
||||
abstract finish(player: IPlayer, cancelled: boolean): void;
|
||||
}
|
||||
|
||||
export enum WorkType {
|
||||
CRIME = "CRIME",
|
||||
CLASS = "CLASS",
|
||||
}
|
||||
|
96
src/Work/WorkStats.ts
Normal file
96
src/Work/WorkStats.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
export interface WorkStats {
|
||||
money: number;
|
||||
reputation: number;
|
||||
hackExp: number;
|
||||
strExp: number;
|
||||
defExp: number;
|
||||
dexExp: number;
|
||||
agiExp: number;
|
||||
chaExp: number;
|
||||
intExp: number;
|
||||
}
|
||||
|
||||
interface newWorkStatsParams {
|
||||
money?: number;
|
||||
reputation?: number;
|
||||
hackExp?: number;
|
||||
strExp?: number;
|
||||
defExp?: number;
|
||||
dexExp?: number;
|
||||
agiExp?: number;
|
||||
chaExp?: number;
|
||||
intExp?: number;
|
||||
}
|
||||
|
||||
export const newWorkStats = (params?: newWorkStatsParams): WorkStats => {
|
||||
return {
|
||||
money: params?.money ?? 0,
|
||||
reputation: params?.reputation ?? 0,
|
||||
hackExp: params?.hackExp ?? 0,
|
||||
strExp: params?.strExp ?? 0,
|
||||
defExp: params?.defExp ?? 0,
|
||||
dexExp: params?.dexExp ?? 0,
|
||||
agiExp: params?.agiExp ?? 0,
|
||||
chaExp: params?.chaExp ?? 0,
|
||||
intExp: params?.intExp ?? 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const sumWorkStats = (w0: WorkStats, w1: WorkStats): WorkStats => {
|
||||
return {
|
||||
money: w0.money + w1.money,
|
||||
reputation: w0.reputation + w1.reputation,
|
||||
hackExp: w0.hackExp + w1.hackExp,
|
||||
strExp: w0.strExp + w1.strExp,
|
||||
defExp: w0.defExp + w1.defExp,
|
||||
dexExp: w0.dexExp + w1.dexExp,
|
||||
agiExp: w0.agiExp + w1.agiExp,
|
||||
chaExp: w0.chaExp + w1.chaExp,
|
||||
intExp: w0.intExp + w1.intExp,
|
||||
};
|
||||
};
|
||||
|
||||
export const scaleWorkStats = (w: WorkStats, n: number): WorkStats => {
|
||||
return {
|
||||
money: w.money * n,
|
||||
reputation: w.reputation * n,
|
||||
hackExp: w.hackExp * n,
|
||||
strExp: w.strExp * n,
|
||||
defExp: w.defExp * n,
|
||||
dexExp: w.dexExp * n,
|
||||
agiExp: w.agiExp * n,
|
||||
chaExp: w.chaExp * n,
|
||||
intExp: w.intExp * n,
|
||||
};
|
||||
};
|
||||
|
||||
export const applyWorkStats = (player: IPlayer, workStats: WorkStats, cycles: number, source: string): WorkStats => {
|
||||
let focusBonus = 1;
|
||||
if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager)) {
|
||||
focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus;
|
||||
}
|
||||
const gains = {
|
||||
money: workStats.money * cycles,
|
||||
reputation: focusBonus * workStats.reputation * cycles,
|
||||
hackExp: focusBonus * workStats.hackExp * cycles,
|
||||
strExp: focusBonus * workStats.strExp * cycles,
|
||||
defExp: focusBonus * workStats.defExp * cycles,
|
||||
dexExp: focusBonus * workStats.dexExp * cycles,
|
||||
agiExp: focusBonus * workStats.agiExp * cycles,
|
||||
chaExp: focusBonus * workStats.chaExp * cycles,
|
||||
intExp: focusBonus * workStats.intExp * cycles,
|
||||
};
|
||||
player.gainHackingExp(gains.hackExp);
|
||||
player.gainStrengthExp(gains.strExp);
|
||||
player.gainDefenseExp(gains.defExp);
|
||||
player.gainDexterityExp(gains.dexExp);
|
||||
player.gainAgilityExp(gains.agiExp);
|
||||
player.gainCharismaExp(gains.chaExp);
|
||||
player.gainIntelligenceExp(gains.intExp);
|
||||
player.gainMoney(gains.money, source);
|
||||
return gains;
|
||||
};
|
47
src/Work/formulas/Class.ts
Normal file
47
src/Work/formulas/Class.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Locations } from "../../Locations/Locations";
|
||||
import { Location } from "../../Locations/Location";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Class, Classes, ClassWork } from "../ClassWork";
|
||||
import { WorkStats } from "../WorkStats";
|
||||
import { Server } from "../../Server/Server";
|
||||
import { GetServer } from "../../Server/AllServers";
|
||||
import { serverMetadata } from "../../Server/data/servers";
|
||||
|
||||
const gameCPS = 1000 / CONSTANTS._idleSpeed; // 5 cycles per second
|
||||
|
||||
export function calculateCost(classs: Class, location: Location): number {
|
||||
const serverMeta = serverMetadata.find((s) => s.specialName === location.name);
|
||||
const server = GetServer(serverMeta ? serverMeta.hostname : "");
|
||||
const discount = (server as Server).backdoorInstalled ? 0.9 : 1;
|
||||
return classs.earnings.money * location.costMult * discount;
|
||||
}
|
||||
|
||||
export function calculateClassEarnings(player: IPlayer, work: ClassWork): WorkStats {
|
||||
//Find cost and exp gain per game cycle
|
||||
const hashManager = player.hashManager;
|
||||
const classs = Classes[work.classType];
|
||||
const location = Locations[work.location];
|
||||
|
||||
const hashMult = work.isGym() ? hashManager.getTrainingMult() : hashManager.getStudyMult();
|
||||
|
||||
const cost = calculateCost(classs, location) / gameCPS;
|
||||
const hackExp = ((classs.earnings.hackExp * location.expMult) / gameCPS) * hashMult;
|
||||
const strExp = ((classs.earnings.strExp * location.expMult) / gameCPS) * hashMult;
|
||||
const defExp = ((classs.earnings.defExp * location.expMult) / gameCPS) * hashMult;
|
||||
const dexExp = ((classs.earnings.dexExp * location.expMult) / gameCPS) * hashMult;
|
||||
const agiExp = ((classs.earnings.agiExp * location.expMult) / gameCPS) * hashMult;
|
||||
const chaExp = ((classs.earnings.chaExp * location.expMult) / gameCPS) * hashMult;
|
||||
return {
|
||||
money: cost,
|
||||
reputation: 0,
|
||||
hackExp: hackExp * player.hacking_exp_mult * BitNodeMultipliers.ClassGymExpGain,
|
||||
strExp: strExp * player.strength_exp_mult * BitNodeMultipliers.ClassGymExpGain,
|
||||
defExp: defExp * player.defense_exp_mult * BitNodeMultipliers.ClassGymExpGain,
|
||||
dexExp: dexExp * player.dexterity_exp_mult * BitNodeMultipliers.ClassGymExpGain,
|
||||
agiExp: agiExp * player.agility_exp_mult * BitNodeMultipliers.ClassGymExpGain,
|
||||
chaExp: chaExp * player.charisma_exp_mult * BitNodeMultipliers.ClassGymExpGain,
|
||||
intExp: 0,
|
||||
};
|
||||
}
|
@ -298,6 +298,7 @@ const Engine: {
|
||||
// Process offline progress
|
||||
loadAllRunningScripts(Player); // This also takes care of offline production for those scripts
|
||||
if (Player.currentWork !== null) {
|
||||
Player.focus = true;
|
||||
Player.processNEWWork(numCyclesOffline);
|
||||
} else if (Player.isWorking) {
|
||||
Player.focus = true;
|
||||
@ -308,9 +309,6 @@ const Engine: {
|
||||
case WorkType.CreateProgram:
|
||||
Player.createProgramWork(numCyclesOffline);
|
||||
break;
|
||||
case WorkType.StudyClass:
|
||||
Player.takeClass(numCyclesOffline);
|
||||
break;
|
||||
case WorkType.CompanyPartTime:
|
||||
Player.workPartTime(numCyclesOffline);
|
||||
break;
|
||||
|
@ -27,6 +27,8 @@ import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { Box, Tooltip } from "@mui/material";
|
||||
|
||||
import { WorkType } from "../../utils/WorkType";
|
||||
import { isClassWork } from "../../Work/ClassWork";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
|
||||
interface IProps {
|
||||
save: () => void;
|
||||
@ -138,12 +140,16 @@ function Work(): React.ReactElement {
|
||||
player.startFocusing();
|
||||
router.toWork();
|
||||
};
|
||||
|
||||
if (!player.isWorking || player.focus) return <></>;
|
||||
if ((!player.isWorking && player.currentWork === null) || player.focus) return <></>;
|
||||
|
||||
let details = <></>;
|
||||
let header = <></>;
|
||||
let innerText = <></>;
|
||||
if (isClassWork(player.currentWork)) {
|
||||
details = <>{player.currentWork.getClass().youAreCurrently}</>;
|
||||
header = <>You are {player.currentWork.getClass().youAreCurrently}</>;
|
||||
innerText = <>{convertTimeMsToTimeElapsedString(player.currentWork.cyclesWorked * CONSTANTS._idleSpeed)}</>;
|
||||
}
|
||||
switch (player.workType) {
|
||||
case WorkType.CompanyPartTime:
|
||||
case WorkType.Company:
|
||||
@ -180,11 +186,6 @@ function Work(): React.ReactElement {
|
||||
</>
|
||||
);
|
||||
break;
|
||||
case WorkType.StudyClass:
|
||||
details = <>{player.workType}</>;
|
||||
header = <>You are {player.className}</>;
|
||||
innerText = <>{convertTimeMsToTimeElapsedString(player.timeWorked)}</>;
|
||||
break;
|
||||
case WorkType.CreateProgram:
|
||||
details = <>Coding {player.createProgramName}</>;
|
||||
header = <>Creating a program</>;
|
||||
|
@ -21,6 +21,8 @@ import { ReputationRate } from "./React/ReputationRate";
|
||||
import { StatsRow } from "./React/StatsRow";
|
||||
import { WorkType, ClassType } from "../utils/WorkType";
|
||||
import { isCrimeWork } from "../Work/CrimeWork";
|
||||
import { isClassWork } from "../Work/ClassWork";
|
||||
import { WorkStats } from "src/Work/WorkStats";
|
||||
|
||||
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
|
||||
|
||||
@ -43,6 +45,89 @@ interface IWorkInfo {
|
||||
stopTooltip?: string | React.ReactElement;
|
||||
}
|
||||
|
||||
export function ExpRows(total: WorkStats, rate: WorkStats): React.ReactElement[] {
|
||||
return [
|
||||
total.hackExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Hacking Exp"
|
||||
color={Settings.theme.hack}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(total.hackExp)} (${numeralWrapper.formatExp(
|
||||
rate.hackExp * CYCLES_PER_SEC,
|
||||
)} / sec)`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
total.strExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Strength Exp"
|
||||
color={Settings.theme.combat}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(total.strExp)} (${numeralWrapper.formatExp(
|
||||
rate.strExp * CYCLES_PER_SEC,
|
||||
)} / sec)`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
total.defExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Defense Exp"
|
||||
color={Settings.theme.combat}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(total.defExp)} (${numeralWrapper.formatExp(
|
||||
rate.defExp * CYCLES_PER_SEC,
|
||||
)} / sec)`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
total.dexExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Dexterity Exp"
|
||||
color={Settings.theme.combat}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(total.dexExp)} (${numeralWrapper.formatExp(
|
||||
rate.dexExp * CYCLES_PER_SEC,
|
||||
)} / sec)`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
total.agiExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Agility Exp"
|
||||
color={Settings.theme.combat}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(total.agiExp)} (${numeralWrapper.formatExp(
|
||||
rate.agiExp * CYCLES_PER_SEC,
|
||||
)} / sec)`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
total.chaExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Charisma Exp"
|
||||
color={Settings.theme.cha}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(total.chaExp)} (${numeralWrapper.formatExp(
|
||||
rate.chaExp * CYCLES_PER_SEC,
|
||||
)} / sec)`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
export function WorkInProgressRoot(): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
@ -57,7 +142,7 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const router = use.Router();
|
||||
|
||||
const expGains = [
|
||||
let expGains = [
|
||||
player.workHackExpGained > 0 ? (
|
||||
<StatsRow
|
||||
name="Hacking Exp"
|
||||
@ -168,6 +253,54 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
stopText: "Cancel crime",
|
||||
};
|
||||
}
|
||||
|
||||
if (isClassWork(player.currentWork)) {
|
||||
const classWork = player.currentWork;
|
||||
function cancel(): void {
|
||||
player.finishNEWWork(true);
|
||||
router.toCity();
|
||||
}
|
||||
|
||||
function unfocus(): void {
|
||||
router.toCity();
|
||||
player.stopFocusing();
|
||||
}
|
||||
|
||||
let stopText = "";
|
||||
if (classWork.isGym()) {
|
||||
stopText = "Stop training at gym";
|
||||
} else {
|
||||
stopText = "Stop taking course";
|
||||
}
|
||||
|
||||
const rates = classWork.calculateRates(player);
|
||||
expGains = ExpRows(classWork.earnings, rates);
|
||||
workInfo = {
|
||||
buttons: {
|
||||
cancel: cancel,
|
||||
unfocus: unfocus,
|
||||
},
|
||||
title: (
|
||||
<>
|
||||
You are currently <b>{classWork.getClass().youAreCurrently}</b>
|
||||
</>
|
||||
),
|
||||
|
||||
gains: [
|
||||
<StatsRow name="Total Cost" color={Settings.theme.money}>
|
||||
<Typography>
|
||||
<Money money={classWork.earnings.money} /> (<MoneyRate money={rates.money * CYCLES_PER_SEC} />)
|
||||
</Typography>
|
||||
</StatsRow>,
|
||||
...expGains,
|
||||
],
|
||||
progress: {
|
||||
elapsed: classWork.cyclesWorked * CONSTANTS._idleSpeed,
|
||||
},
|
||||
|
||||
stopText: stopText,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
switch (player.workType) {
|
||||
@ -240,60 +373,6 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
break;
|
||||
}
|
||||
|
||||
case WorkType.StudyClass: {
|
||||
const className = player.className;
|
||||
function cancel(): void {
|
||||
player.finishClass(true);
|
||||
router.toCity();
|
||||
}
|
||||
|
||||
function unfocus(): void {
|
||||
router.toCity();
|
||||
player.stopFocusing();
|
||||
}
|
||||
|
||||
let stopText = "";
|
||||
if (
|
||||
className === ClassType.GymStrength ||
|
||||
className === ClassType.GymDefense ||
|
||||
className === ClassType.GymDexterity ||
|
||||
className === ClassType.GymAgility
|
||||
) {
|
||||
stopText = "Stop training at gym";
|
||||
} else {
|
||||
stopText = "Stop taking course";
|
||||
}
|
||||
|
||||
workInfo = {
|
||||
buttons: {
|
||||
cancel: cancel,
|
||||
unfocus: unfocus,
|
||||
},
|
||||
title: (
|
||||
<>
|
||||
You are currently <b>{className}</b>
|
||||
</>
|
||||
),
|
||||
|
||||
gains: [
|
||||
<StatsRow name="Total Cost" color={Settings.theme.money}>
|
||||
<Typography>
|
||||
<Money money={-player.workMoneyGained} /> (<MoneyRate money={player.workMoneyLossRate * CYCLES_PER_SEC} />
|
||||
)
|
||||
</Typography>
|
||||
</StatsRow>,
|
||||
...expGains,
|
||||
],
|
||||
progress: {
|
||||
elapsed: player.timeWorked,
|
||||
},
|
||||
|
||||
stopText: stopText,
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case WorkType.Company: {
|
||||
const comp = Companies[player.companyName];
|
||||
if (comp == null || !(comp instanceof Company)) {
|
||||
|
Loading…
Reference in New Issue
Block a user