COMPANY: Much better job location interface (#927)

This commit is contained in:
Jesse Clark 2023-12-18 04:23:47 -08:00 committed by GitHub
parent 97d679bdac
commit 28ef5df880
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 443 additions and 798 deletions

@ -56,42 +56,6 @@ export class Company {
return this.companyPositions.has(typeof pos === "string" ? pos : pos.name);
}
hasAgentPositions(): boolean {
return this.companyPositions.has(JobName.agent0);
}
hasBusinessConsultantPositions(): boolean {
return this.companyPositions.has(JobName.businessConsult0);
}
hasBusinessPositions(): boolean {
return this.companyPositions.has(JobName.business0);
}
hasEmployeePositions(): boolean {
return this.companyPositions.has(JobName.employee);
}
hasITPositions(): boolean {
return this.companyPositions.has(JobName.IT0);
}
hasSecurityPositions(): boolean {
return this.companyPositions.has(JobName.security0);
}
hasSoftwareConsultantPositions(): boolean {
return this.companyPositions.has(JobName.softwareConsult0);
}
hasSoftwarePositions(): boolean {
return this.companyPositions.has(JobName.software0);
}
hasWaiterPositions(): boolean {
return this.companyPositions.has(JobName.waiter);
}
prestigeAugmentation(): void {
if (this.favor == null) this.favor = 0;
this.favor += this.getFavorGain();
@ -121,7 +85,7 @@ export class Company {
return Generic_fromJSON(Company, value.data, Company.includedKeys);
}
// Only these 3 keys are relevant to the save file
// Only these 2 keys are relevant to the save file
static includedKeys = ["favor", "playerReputation"] as const;
}

@ -1,22 +1,16 @@
import { Person as IPerson } from "@nsdefs";
import { CONSTANTS } from "../Constants";
import { JobName, JobField } from "@enums";
import {
agentJobs,
businessConsultJobs,
businessJobs,
itJobs,
netEngJobs,
securityJobs,
softwareConsultJobs,
softwareJobs,
} from "./data/JobTracks";
import type { Skills } from "../PersonObjects/Skills";
export interface CompanyPositionCtorParams {
nextPosition: JobName | null;
field: JobField;
baseSalary: number;
repMultiplier: number;
applyText?: string;
hiredText?: string;
isPartTime?: boolean;
reqdHacking?: number;
reqdStrength?: number;
@ -60,6 +54,15 @@ export class CompanyPosition {
/** Reputation multiplier */
repMultiplier: number;
/** Text to display when applying for this job */
applyText: string;
/** Text to display when receiving this job */
hiredText: string;
/** Whether this position is part-time */
isPartTime: boolean;
/** Required stats to earn this position */
requiredAgility: number;
requiredCharisma: number;
@ -93,6 +96,9 @@ export class CompanyPosition {
this.nextPosition = p.nextPosition;
this.baseSalary = p.baseSalary;
this.repMultiplier = p.repMultiplier;
this.isPartTime = p.isPartTime ?? false;
this.applyText = p.applyText ?? `Apply for ${this.name} Job`;
this.hiredText = p.hiredText ?? `Congratulations, you are now employed as a ${this.name}`;
this.requiredHacking = p.reqdHacking != null ? p.reqdHacking : 0;
this.requiredStrength = p.reqdStrength != null ? p.reqdStrength : 0;
@ -130,6 +136,18 @@ export class CompanyPosition {
this.charismaExpGain = p.charismaExpGain != null ? p.charismaExpGain : 0;
}
requiredSkills(jobStatReqOffset: number): Skills {
return {
hacking: this.requiredHacking > 0 ? this.requiredHacking + jobStatReqOffset : 0,
strength: this.requiredStrength > 0 ? this.requiredStrength + jobStatReqOffset : 0,
defense: this.requiredDefense > 0 ? this.requiredDefense + jobStatReqOffset : 0,
dexterity: this.requiredDexterity > 0 ? this.requiredDexterity + jobStatReqOffset : 0,
agility: this.requiredAgility > 0 ? this.requiredAgility + jobStatReqOffset : 0,
charisma: this.requiredCharisma > 0 ? this.requiredCharisma + jobStatReqOffset : 0,
intelligence: 0,
};
}
calculateJobPerformance(worker: IPerson): number {
const hackRatio: number = (this.hackingEffectiveness * worker.skills.hacking) / CONSTANTS.MaxSkillLevel;
const strRatio: number = (this.strengthEffectiveness * worker.skills.strength) / CONSTANTS.MaxSkillLevel;
@ -147,44 +165,4 @@ export class CompanyPosition {
reputationGain += worker.skills.intelligence / CONSTANTS.MaxSkillLevel;
return reputationGain;
}
isSoftwareJob(): boolean {
return softwareJobs.includes(this.name);
}
isITJob(): boolean {
return itJobs.includes(this.name);
}
isSecurityEngineerJob(): boolean {
return this.name === JobName.securityEng;
}
isNetworkEngineerJob(): boolean {
return netEngJobs.includes(this.name);
}
isBusinessJob(): boolean {
return businessJobs.includes(this.name);
}
isSecurityJob(): boolean {
return securityJobs.includes(this.name);
}
isAgentJob(): boolean {
return agentJobs.includes(this.name);
}
isSoftwareConsultantJob(): boolean {
return softwareConsultJobs.includes(this.name);
}
isBusinessConsultantJob(): boolean {
return businessConsultJobs.includes(this.name);
}
isPartTimeJob(): boolean {
return [JobName.employeePT, JobName.waiterPT].includes(this.name);
}
}

@ -1,51 +0,0 @@
import { Company } from "./Company";
import { CompanyPosition } from "./CompanyPosition";
/** Returns a string with the given CompanyPosition's stat requirements */
export function getJobRequirementText(company: Company, pos: CompanyPosition, tooltiptext = false): string {
let reqText = "";
const offset: number = company.jobStatReqOffset;
const reqHacking: number = pos.requiredHacking > 0 ? pos.requiredHacking + offset : 0;
const reqStrength: number = pos.requiredStrength > 0 ? pos.requiredStrength + offset : 0;
const reqDefense: number = pos.requiredDefense > 0 ? pos.requiredDefense + offset : 0;
const reqDexterity: number = pos.requiredDexterity > 0 ? pos.requiredDexterity + offset : 0;
const reqAgility: number = pos.requiredDexterity > 0 ? pos.requiredDexterity + offset : 0;
const reqCharisma: number = pos.requiredCharisma > 0 ? pos.requiredCharisma + offset : 0;
const reqRep: number = pos.requiredReputation;
if (tooltiptext) {
reqText = "Requires:<br>";
reqText += reqHacking.toString() + " hacking<br>";
reqText += reqStrength.toString() + " strength<br>";
reqText += reqDefense.toString() + " defense<br>";
reqText += reqDexterity.toString() + " dexterity<br>";
reqText += reqAgility.toString() + " agility<br>";
reqText += reqCharisma.toString() + " charisma<br>";
reqText += reqRep.toString() + " reputation";
} else {
reqText = "(Requires ";
if (reqHacking > 0) {
reqText += reqHacking + " hacking, ";
}
if (reqStrength > 0) {
reqText += reqStrength + " strength, ";
}
if (reqDefense > 0) {
reqText += reqDefense + " defense, ";
}
if (reqDexterity > 0) {
reqText += reqDexterity + " dexterity, ";
}
if (reqAgility > 0) {
reqText += reqAgility + " agility, ";
}
if (reqCharisma > 0) {
reqText += reqCharisma + " charisma, ";
}
if (reqRep > 1) {
reqText += reqRep + " reputation, ";
}
reqText = reqText.substring(0, reqText.length - 2);
reqText += ")";
}
return reqText;
}

@ -0,0 +1,23 @@
import { Company } from "./Company";
import { CompanyPosition } from "./CompanyPosition";
import { PlayerCondition, haveSkill, haveCompanyRep } from "../Faction/FactionJoinCondition";
import type { Skills } from "../PersonObjects/Skills";
export function getJobRequirements(company: Company, pos: CompanyPosition): PlayerCondition[] {
const reqSkills = pos.requiredSkills(company.jobStatReqOffset);
const reqs = [];
for (const [skillName, value] of Object.entries(reqSkills)) {
if (value > 0) reqs.push(haveSkill(skillName as keyof Skills, value));
}
if (pos.requiredReputation > 0) {
reqs.push(haveCompanyRep(company.name, pos.requiredReputation));
}
return reqs;
}
/** Returns a string with the given CompanyPosition's stat requirements */
export function getJobRequirementText(company: Company, pos: CompanyPosition): string {
const reqs = getJobRequirements(company, pos);
return `(${pos.name} requires: ${reqs.map((s) => s.toString()).join(", ")})`;
}

@ -1,14 +1,15 @@
import { CompanyCtorParams } from "../Company";
import { CompanyName, JobName, FactionName } from "@enums";
import { CompanyName, FactionName, JobName } from "@enums";
import {
agentJobs,
softwareJobs,
businessJobs,
itJobs,
netEngJobs,
securityJobs,
softwareConsultJobs,
softwareJobs,
netEngJobs,
itJobs,
agentJobs,
businessConsultJobs,
} from "./JobTracks";
export function getCompaniesMetadata(): Record<CompanyName, CompanyCtorParams> {
@ -107,14 +108,14 @@ export function getCompaniesMetadata(): Record<CompanyName, CompanyCtorParams> {
},
[CompanyName.DefComm]: {
name: CompanyName.DefComm,
companyPositions: [JobName.business5, ...allTechJobs, ...softwareConsultJobs],
companyPositions: [JobName.business5, ...allTechJobs, ...softwareConsultJobs, ...businessConsultJobs],
expMultiplier: 1.75,
salaryMultiplier: 1.75,
jobStatReqOffset: 199,
},
[CompanyName.HeliosLabs]: {
name: CompanyName.HeliosLabs,
companyPositions: [JobName.business5, ...allTechJobs, ...softwareConsultJobs],
companyPositions: [JobName.business5, ...allTechJobs, ...softwareConsultJobs, ...businessConsultJobs],
expMultiplier: 1.8,
salaryMultiplier: 1.8,
jobStatReqOffset: 199,
@ -149,28 +150,28 @@ export function getCompaniesMetadata(): Record<CompanyName, CompanyCtorParams> {
},
[CompanyName.AeroCorp]: {
name: CompanyName.AeroCorp,
companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs],
companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs, ...businessConsultJobs],
expMultiplier: 1.7,
salaryMultiplier: 1.7,
jobStatReqOffset: 199,
},
[CompanyName.OmniaCybersystems]: {
name: CompanyName.OmniaCybersystems,
companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs],
companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs, ...businessConsultJobs],
expMultiplier: 1.7,
salaryMultiplier: 1.7,
jobStatReqOffset: 199,
},
[CompanyName.SolarisSpaceSystems]: {
name: CompanyName.SolarisSpaceSystems,
companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs],
companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs, ...businessConsultJobs],
expMultiplier: 1.7,
salaryMultiplier: 1.7,
jobStatReqOffset: 199,
},
[CompanyName.DeltaOne]: {
name: CompanyName.DeltaOne,
companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs],
companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs, ...businessConsultJobs],
expMultiplier: 1.6,
salaryMultiplier: 1.6,
jobStatReqOffset: 199,

@ -65,6 +65,7 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
reqdHacking: 501,
reqdReputation: 400e3,
repMultiplier: 1.6,
hiredText: `Congratulations, you are now ${JobName.software4}`,
},
[JobName.software5]: {
nextPosition: JobName.software6, // Vice President of Technology
@ -78,6 +79,7 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
reqdHacking: 501,
reqdReputation: 800e3,
repMultiplier: 1.6,
hiredText: `Congratulations, you are now ${JobName.software5}`,
},
[JobName.software6]: {
nextPosition: JobName.software7, // Chief Technology Officer
@ -91,6 +93,7 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
reqdHacking: 601,
reqdReputation: 1.6e6,
repMultiplier: 1.75,
hiredText: `Congratulations, you are now ${JobName.software6}`,
},
[JobName.software7]: {
nextPosition: null,
@ -104,6 +107,7 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
reqdHacking: 751,
reqdReputation: 3.2e6,
repMultiplier: 2,
hiredText: `Congratulations, you are now ${JobName.software7}`,
},
[JobName.IT0]: {
nextPosition: JobName.IT1, // IT Analyst
@ -256,6 +260,7 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
reqdHacking: 76,
reqdReputation: 800e3,
repMultiplier: 1.6,
hiredText: `Congratulations, you are now ${JobName.business4}`,
},
[JobName.business5]: {
nextPosition: null,
@ -269,6 +274,7 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
reqdHacking: 101,
reqdReputation: 3.2e6,
repMultiplier: 1.75,
hiredText: `Congratulations, you are now ${JobName.business5}`,
},
[JobName.security0]: {
nextPosition: JobName.security1, // Security Officer
@ -457,6 +463,8 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
agilityExpGain: 0.02,
charismaExpGain: 0.05,
repMultiplier: 1,
applyText: `Apply to be a ${JobName.waiter}`,
hiredText: `Congratulations, you are now employed as a ${JobName.waiter}`,
},
[JobName.employee]: {
nextPosition: null,
@ -472,6 +480,8 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
agilityExpGain: 0.02,
charismaExpGain: 0.04,
repMultiplier: 1,
applyText: `Apply to be an ${JobName.employee}`,
hiredText: "Congratulations, you are now employed",
},
[JobName.softwareConsult0]: {
nextPosition: JobName.softwareConsult1, // Senior Software Consultant
@ -483,6 +493,8 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
charismaExpGain: 0.03,
reqdHacking: 51,
repMultiplier: 1,
applyText: `Pitch a Software Consulting contract`,
hiredText: `Congratulations, you got a contract as a ${JobName.softwareConsult0}`,
},
[JobName.softwareConsult1]: {
nextPosition: null,
@ -495,6 +507,8 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
reqdHacking: 251,
reqdCharisma: 51,
repMultiplier: 1.2,
applyText: `Pitch a Software Consulting contract`,
hiredText: `Congratulations, you got a contract as a ${JobName.softwareConsult1}`,
},
[JobName.businessConsult0]: {
nextPosition: JobName.businessConsult1, // Senior Business Consultant
@ -507,6 +521,8 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
reqdHacking: 6,
reqdCharisma: 51,
repMultiplier: 1,
applyText: `Pitch a Business Consulting contract`,
hiredText: `Congratulations, you got a contract as a ${JobName.businessConsult0}`,
},
[JobName.businessConsult1]: {
nextPosition: null,
@ -519,10 +535,12 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
reqdHacking: 51,
reqdCharisma: 226,
repMultiplier: 1.2,
applyText: `Pitch a Business Consulting contract`,
hiredText: `Congratulations, you got a contract as a ${JobName.businessConsult1}`,
},
[JobName.waiterPT]: {
nextPosition: null,
field: JobField.partTimeWaiter,
field: JobField.waiter,
baseSalary: 20,
strengthEffectiveness: 10,
dexterityEffectiveness: 10,
@ -534,10 +552,13 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
agilityExpGain: 0.0075,
charismaExpGain: 0.04,
repMultiplier: 1,
isPartTime: true,
applyText: `Apply to be a ${JobName.waiterPT}`,
hiredText: `Congratulations, you are now employed as a ${JobName.waiterPT}`,
},
[JobName.employeePT]: {
nextPosition: null,
field: JobField.partTimeEmployee,
field: JobField.employee,
baseSalary: 20,
strengthEffectiveness: 10,
dexterityEffectiveness: 10,
@ -549,6 +570,9 @@ export function getCompanyPositionMetadata(): Record<JobName, CompanyPositionCto
agilityExpGain: 0.0075,
charismaExpGain: 0.03,
repMultiplier: 1,
isPartTime: true,
applyText: `Apply to be a ${JobName.employeePT}`,
hiredText: "Congratulations, you are now employed part-time",
},
};
}

@ -1,5 +1,7 @@
import { JobName } from "@enums";
export const softwareJobs = [
import { JobName, JobField } from "@enums";
export const JobTracks: Record<JobField, readonly JobName[]> = {
[JobField.software]: [
JobName.software0,
JobName.software1,
JobName.software2,
@ -8,18 +10,33 @@ export const softwareJobs = [
JobName.software5,
JobName.software6,
JobName.software7,
];
export const itJobs = [JobName.IT0, JobName.IT1, JobName.IT2, JobName.IT3];
export const netEngJobs = [JobName.networkEng0, JobName.networkEng1];
export const businessJobs = [
],
[JobField.softwareConsultant]: [JobName.softwareConsult0, JobName.softwareConsult1],
[JobField.it]: [JobName.IT0, JobName.IT1, JobName.IT2, JobName.IT3],
[JobField.securityEngineer]: [JobName.securityEng],
[JobField.networkEngineer]: [JobName.networkEng0, JobName.networkEng1],
[JobField.business]: [
JobName.business0,
JobName.business1,
JobName.business2,
JobName.business3,
JobName.business4,
JobName.business5,
];
export const securityJobs = [JobName.security0, JobName.security1, JobName.security2, JobName.security3];
export const agentJobs = [JobName.agent0, JobName.agent1, JobName.agent2];
export const softwareConsultJobs = [JobName.softwareConsult0, JobName.softwareConsult1];
export const businessConsultJobs = [JobName.businessConsult0, JobName.businessConsult1];
],
[JobField.businessConsultant]: [JobName.businessConsult0, JobName.businessConsult1],
[JobField.security]: [JobName.security0, JobName.security1, JobName.security2, JobName.security3],
[JobField.agent]: [JobName.agent0, JobName.agent1, JobName.agent2],
[JobField.employee]: [JobName.employee],
[JobField.partTimeEmployee]: [JobName.employeePT],
[JobField.waiter]: [JobName.waiter],
[JobField.partTimeWaiter]: [JobName.waiterPT],
} as const;
export const softwareJobs = JobTracks[JobField.software];
export const itJobs = JobTracks[JobField.it];
export const netEngJobs = JobTracks[JobField.networkEngineer];
export const businessJobs = JobTracks[JobField.business];
export const securityJobs = JobTracks[JobField.security];
export const agentJobs = JobTracks[JobField.agent];
export const softwareConsultJobs = JobTracks[JobField.softwareConsultant];
export const businessConsultJobs = JobTracks[JobField.businessConsultant];

@ -0,0 +1,74 @@
import * as React from "react";
import { Company } from "../Company";
import { CompanyPosition } from "../CompanyPosition";
import { Player } from "@player";
import { Typography } from "@mui/material";
import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip";
import { CompanyPositions } from "../CompanyPositions";
import { JobSummary } from "./JobSummary";
import { Requirement } from "../../ui/Components/Requirement";
import { getJobRequirements } from "../GetJobRequirements";
interface IProps {
company: Company;
position: CompanyPosition;
currentPosition: CompanyPosition | null;
}
/** React Component for a button that's used to apply for a job */
export function ApplyToJobButton(props: IProps): React.ReactElement {
const underqualified = !Player.isQualified(props.company, props.position);
const nextPos = props.position.nextPosition && CompanyPositions[props.position.nextPosition];
const overqualified = nextPos != null && Player.isQualified(props.company, nextPos);
const reqs = getJobRequirements(props.company, props.position);
const positionRequirements =
reqs.length == 0 ? (
<Typography>Accepting all applicants</Typography>
) : (
<>
<Typography>Requirements:</Typography>
{reqs.map((req, i) => (
<Requirement key={i} fulfilled={req.isSatisfied(Player)} value={req.toString()} />
))}
</>
);
const positionDetails = (
<>
<JobSummary company={props.company} position={props.position} overqualified={overqualified} />
{props.position.isPartTime && (
<Typography>
<br />
Part-time jobs have no penalty for
<br /> doing something else simultaneously.
</Typography>
)}
<br />
{positionRequirements}
{overqualified && (
<Typography>
<br />
You are overqualified for this position.
</Typography>
)}
</>
);
function applyForJob(): void {
Player.applyForJob(props.company, props.position);
}
return (
<ButtonWithTooltip
disabledTooltip={underqualified && positionDetails}
normalTooltip={positionDetails}
onClick={applyForJob}
tooltipProps={{ style: { display: "grid" } }}
>
{props.position.applyText}
</ButtonWithTooltip>
);
}

@ -0,0 +1,40 @@
import type { Company } from "../Company";
import type { CompanyPosition } from "../CompanyPosition";
import React from "react";
import { CompanyPositions } from "../CompanyPositions";
import { ApplyToJobButton } from "./ApplyToJobButton";
import { Player } from "@player";
interface IJobListingsProps {
company: Company;
currentPosition: CompanyPosition | null;
}
export function JobListings(props: IJobListingsProps): React.ReactElement {
const { company, currentPosition } = props;
const jobsToShow = [];
for (const jobName of company.companyPositions) {
const offeredPos = CompanyPositions[jobName];
const underqualified = !Player.isQualified(props.company, offeredPos);
const isCurrentPosition = jobName == props.currentPosition?.name;
const nextPos = offeredPos.nextPosition && CompanyPositions[offeredPos.nextPosition];
const overqualified = nextPos != null && Player.isQualified(props.company, nextPos);
const shouldShowApplyButton =
!isCurrentPosition && !overqualified && (!underqualified || offeredPos.requiredReputation == 0);
if (shouldShowApplyButton) {
jobsToShow.push(offeredPos);
}
}
return (
<>
{jobsToShow.map((position) => (
<ApplyToJobButton key={position.name} company={company} position={position} currentPosition={currentPosition} />
))}
</>
);
}

@ -0,0 +1,35 @@
import { Typography } from "@mui/material";
import { Player } from "@player";
import * as React from "react";
import { CONSTANTS } from "../../Constants";
import { calculateCompanyWorkStats } from "../../Work/Formulas";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { ReputationRate } from "../../ui/React/ReputationRate";
import { StatsTable } from "../../ui/React/StatsTable";
import type { Company } from "../Company";
import type { CompanyPosition } from "../CompanyPosition";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
interface IJobSummaryProps {
company: Company;
position: CompanyPosition;
overqualified?: boolean;
}
export function JobSummary(props: IJobSummaryProps): React.ReactElement {
const workStats = calculateCompanyWorkStats(Player, props.company, props.position, props.company.favor);
return (
<>
<Typography>
<u>{props.position.name}</u>
</Typography>
<StatsTable
wide
rows={[
["Wages:", <MoneyRate key="money" money={workStats.money * CYCLES_PER_SEC} />],
["Reputation:", <ReputationRate key="rep" reputation={workStats.reputation * CYCLES_PER_SEC} />],
]}
/>
</>
);
}

@ -43,6 +43,20 @@ export function ServersDev(): React.ReactElement {
}
}
function backdoorServer(): void {
const s = GetServer(server);
if (s === null) return;
if (!(s instanceof Server)) return;
s.backdoorInstalled = true;
}
function backdoorAllServers(): void {
for (const s of GetAllServers()) {
if (!(s instanceof Server)) return;
s.backdoorInstalled = true;
}
}
function minSecurity(): void {
const s = GetServer(server);
if (s === null) return;
@ -118,6 +132,17 @@ export function ServersDev(): React.ReactElement {
<Button onClick={rootAllServers}>Root all</Button>
</td>
</tr>
<tr>
<td>
<Typography>Backdoor:</Typography>
</td>
<td>
<Button onClick={backdoorServer}>Backdoor one</Button>
</td>
<td>
<Button onClick={backdoorAllServers}>Backdoor all</Button>
</td>
</tr>
<tr>
<td>
<Typography>Security:</Typography>

@ -1,40 +0,0 @@
import * as React from "react";
import { Company } from "../../Company/Company";
import { CompanyPosition } from "../../Company/CompanyPosition";
import { getJobRequirementText } from "../../Company/GetJobRequirementText";
import { Player } from "@player";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
interface IProps {
company: Company;
entryPosType: CompanyPosition;
onClick: (e: React.MouseEvent<HTMLElement>) => void;
text: string;
}
/** React Component for a button that's used to apply for a job */
export function ApplyToJobButton(props: IProps): React.ReactElement {
function getJobRequirementTooltip(): string {
const pos = Player.getNextCompanyPosition(props.company, props.entryPosType);
if (pos == null) {
return "";
}
if (!props.company.hasPosition(pos)) {
return "";
}
return getJobRequirementText(props.company, pos, true);
}
return (
<>
<Tooltip title={<span dangerouslySetInnerHTML={{ __html: getJobRequirementTooltip() }}></span>}>
<Button onClick={props.onClick}>{props.text}</Button>
</Tooltip>
</>
);
}

@ -4,15 +4,10 @@
* This subcomponent renders all of the buttons for applying to jobs at a company
*/
import React, { useState } from "react";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
import Box from "@mui/material/Box";
import { ApplyToJobButton } from "./ApplyToJobButton";
import { Paper, Box, Tooltip, Button, Typography } from "@mui/material";
import { Locations } from "../Locations";
import { CompanyName, JobName, JobField } from "@enums";
import { CompanyName } from "@enums";
import { Companies } from "../../Company/Companies";
import { CompanyPositions } from "../../Company/CompanyPositions";
@ -26,6 +21,9 @@ import { QuitJobModal } from "../../Company/ui/QuitJobModal";
import { CompanyWork } from "../../Work/CompanyWork";
import { useRerender } from "../../ui/React/hooks";
import { companyNameAsLocationName } from "../../Company/utils";
import { JobSummary } from "../../Company/ui/JobSummary";
import { StatsTable } from "../../ui/React/StatsTable";
import { JobListings } from "../../Company/ui/JobListings";
interface IProps {
companyName: CompanyName;
@ -54,101 +52,12 @@ export function CompanyLocation(props: IProps): React.ReactElement {
const hasMoreJobs = Object.keys(Player.jobs).length > 1;
/**
* CompanyPosition object for the job that the player holds at this company
* (if he has one)
* CompanyPosition object for the job that the player holds at this company, if applicable
*/
const companyPosition = jobTitle ? CompanyPositions[jobTitle] : null;
const currentPosition = jobTitle ? CompanyPositions[jobTitle] : null;
Player.location = companyNameAsLocationName(props.companyName);
function applyForAgentJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForAgentJob();
rerender();
}
function applyForBusinessConsultantJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForBusinessConsultantJob();
rerender();
}
function applyForBusinessJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForBusinessJob();
rerender();
}
function applyForEmployeeJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForEmployeeJob();
rerender();
}
function applyForItJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForItJob();
rerender();
}
function applyForPartTimeEmployeeJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForPartTimeEmployeeJob();
rerender();
}
function applyForPartTimeWaiterJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForPartTimeWaiterJob();
rerender();
}
function applyForSecurityJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForSecurityJob();
rerender();
}
function applyForSoftwareConsultantJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForSoftwareConsultantJob();
rerender();
}
function applyForSoftwareJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForSoftwareJob();
rerender();
}
function applyForWaiterJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Player.applyForWaiterJob();
rerender();
}
function startInfiltration(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
@ -164,8 +73,7 @@ export function CompanyLocation(props: IProps): React.ReactElement {
return;
}
const pos = companyPosition;
if (pos) {
if (currentPosition) {
Player.startWork(
new CompanyWork({
singularity: false,
@ -189,26 +97,27 @@ export function CompanyLocation(props: IProps): React.ReactElement {
Router.toPage(Page.Job, { location: Locations[Object.keys(Player.jobs)[targetNum]] });
}
const isEmployedHere = jobTitle != null;
const isEmployedHere = currentPosition != null;
const favorGain = company.getFavorGain();
return (
<>
<Box sx={{ display: "grid", width: "fit-content", minWidth: "25em" }}>
{isEmployedHere && hasMoreJobs && (
<>
<Box>
<Button onClick={() => switchLoc(-1)}>Previous</Button>
<Button onClick={() => switchLoc(1)}>Next</Button>
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
<Button onClick={() => switchLoc(-1)}>Previous Job</Button>
<Button onClick={() => switchLoc(1)}>Next Job</Button>
</Box>
<br />
</>
)}
{isEmployedHere && (
<>
<Typography>Job Title: {jobTitle}</Typography>
<Typography>-------------------------</Typography>
<Box display="flex">
<Paper sx={{ p: "0.5em 1em", mt: 2, mb: 2 }}>
<JobSummary company={company} position={currentPosition} />
<StatsTable
wide
rows={[
[
<Tooltip
key="repLabel"
title={
<>
You will have <Favor favor={company.favor + favorGain} /> company favor upon resetting after
@ -216,32 +125,30 @@ export function CompanyLocation(props: IProps): React.ReactElement {
</>
}
>
<Typography>
Company reputation: <Reputation reputation={company.playerReputation} />
</Typography>
</Tooltip>
</Box>
<Typography>-------------------------</Typography>
<Box display="flex">
<Typography>Total reputation:</Typography>
</Tooltip>,
<Reputation key="rep" reputation={company.playerReputation} />,
],
[
<Tooltip
key="favorLabel"
title={
<>
Company favor increases the rate at which you earn reputation for this company by 1% per favor.
Company favor is gained whenever you reset after installing Augmentations. The amount of favor you
gain depends on how much reputation you have with the company.
Company favor is gained whenever you reset after installing Augmentations. The amount of favor
you gain depends on how much reputation you have with the company.
</>
}
>
<Typography>
Company Favor: <Favor favor={company.favor} />
</Typography>
</Tooltip>
</Box>
<Typography>-------------------------</Typography>
<br />
</>
<Typography>Company favor:</Typography>
</Tooltip>,
<Favor key="favor" favor={company.favor} />,
],
]}
/>
</Paper>
)}
<Box sx={{ display: "grid", width: "fit-content" }}>
{isEmployedHere && (
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
<Button onClick={work}>Work</Button>
@ -255,94 +162,9 @@ export function CompanyLocation(props: IProps): React.ReactElement {
/>
</Box>
)}
{company.hasAgentPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.agent0]}
onClick={applyForAgentJob}
text={"Apply for " + JobField.agent + " Job"}
/>
)}
{company.hasBusinessConsultantPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.businessConsult0]}
onClick={applyForBusinessConsultantJob}
text={"Apply for " + JobField.businessConsultant + " Job"}
/>
)}
{company.hasBusinessPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.business0]}
onClick={applyForBusinessJob}
text={"Apply for " + JobField.business + " Job"}
/>
)}
{company.hasEmployeePositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.employee]}
onClick={applyForEmployeeJob}
text={"Apply to be an " + JobField.employee}
/>
)}
{company.hasEmployeePositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.employeePT]}
onClick={applyForPartTimeEmployeeJob}
text={"Apply to be a " + JobField.partTimeEmployee}
/>
)}
{company.hasITPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.IT0]}
onClick={applyForItJob}
text={"Apply for " + JobField.it + " Job"}
/>
)}
{company.hasSecurityPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.security0]}
onClick={applyForSecurityJob}
text={"Apply for " + JobField.security + " Job"}
/>
)}
{company.hasSoftwareConsultantPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.softwareConsult0]}
onClick={applyForSoftwareConsultantJob}
text={"Apply for " + JobField.softwareConsultant + " Job"}
/>
)}
{company.hasSoftwarePositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.software0]}
onClick={applyForSoftwareJob}
text={"Apply for " + JobField.software + " Job"}
/>
)}
{company.hasWaiterPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.waiter]}
onClick={applyForWaiterJob}
text={"Apply to be a " + JobField.waiter}
/>
)}
{company.hasWaiterPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[JobName.waiterPT]}
onClick={applyForPartTimeWaiterJob}
text={"Apply to be a " + JobField.partTimeWaiter}
/>
)}
{company.companyPositions.size > 0 && <JobListings company={company} currentPosition={currentPosition} />}
{location.infiltrationData != null && <Button onClick={startInfiltration}>Infiltrate Company</Button>}
</Box>
</>

@ -8,7 +8,6 @@ import {
FactionName,
FactionWorkType,
GymType,
JobField,
LocationName,
UniversityClassType,
} from "@enums";
@ -55,8 +54,8 @@ import { Engine } from "../engine";
import { getEnumHelper } from "../utils/EnumHelper";
import { ScriptFilePath, resolveScriptFilePath } from "../Paths/ScriptFilePath";
import { root } from "../Paths/Directory";
import { companyNameAsLocationName } from "../Company/utils";
import { getRecordEntries } from "../Types/Record";
import { JobTracks } from "../Company/data/JobTracks";
export function NetscriptSingularity(): InternalAPI<ISingularity> {
const runAfterReset = function (cbScript: ScriptFilePath) {
@ -687,20 +686,12 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
const job = CompanyPositions[positionName];
const res = {
name: CompanyPositions[positionName].name,
field: CompanyPositions[positionName].field,
nextPosition: CompanyPositions[positionName].nextPosition,
salary: CompanyPositions[positionName].baseSalary * company.salaryMultiplier,
requiredReputation: CompanyPositions[positionName].requiredReputation,
requiredSkills: {
hacking: job.requiredHacking > 0 ? job.requiredHacking + company.jobStatReqOffset : 0,
strength: job.requiredStrength > 0 ? job.requiredStrength + company.jobStatReqOffset : 0,
defense: job.requiredDefense > 0 ? job.requiredDefense + company.jobStatReqOffset : 0,
dexterity: job.requiredDexterity > 0 ? job.requiredDexterity + company.jobStatReqOffset : 0,
agility: job.requiredAgility > 0 ? job.requiredAgility + company.jobStatReqOffset : 0,
charisma: job.requiredCharisma > 0 ? job.requiredCharisma + company.jobStatReqOffset : 0,
intelligence: 0,
},
name: job.name,
field: job.field,
nextPosition: job.nextPosition,
salary: job.baseSalary * company.salaryMultiplier,
requiredReputation: job.requiredReputation,
requiredSkills: job.requiredSkills(company.jobStatReqOffset),
};
return res;
},
@ -739,62 +730,16 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
helpers.checkSingularityAccess(ctx);
const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName);
const field = getEnumHelper("JobField").nsGetMember(ctx, _field, "field", { fuzzy: true });
const company = Companies[companyName];
const entryPos = CompanyPositions[JobTracks[field][0]];
Player.location = companyNameAsLocationName(companyName);
let res;
switch (field) {
case JobField.software:
res = Player.applyForSoftwareJob(true);
break;
case JobField.softwareConsultant:
res = Player.applyForSoftwareConsultantJob(true);
break;
case JobField.it:
res = Player.applyForItJob(true);
break;
case JobField.securityEngineer:
res = Player.applyForSecurityEngineerJob(true);
break;
case JobField.networkEngineer:
res = Player.applyForNetworkEngineerJob(true);
break;
case JobField.business:
res = Player.applyForBusinessJob(true);
break;
case JobField.businessConsultant:
res = Player.applyForBusinessConsultantJob(true);
break;
case JobField.security:
res = Player.applyForSecurityJob(true);
break;
case JobField.agent:
res = Player.applyForAgentJob(true);
break;
case JobField.employee:
res = Player.applyForEmployeeJob(true);
break;
case JobField.partTimeEmployee:
res = Player.applyForPartTimeEmployeeJob(true);
break;
case JobField.waiter:
res = Player.applyForWaiterJob(true);
break;
case JobField.partTimeWaiter:
res = Player.applyForPartTimeWaiterJob(true);
break;
default:
helpers.log(ctx, () => `Invalid job: '${field}'.`);
return false;
}
if (res) {
helpers.log(
ctx,
() => `You were offered a new job at '${companyName}' with position '${Player.jobs[companyName]}'`,
);
const jobName = Player.applyForJob(company, entryPos, true);
if (jobName) {
helpers.log(ctx, () => `You were offered a new job at '${companyName}' with position '${jobName}'`);
} else {
helpers.log(ctx, () => `You failed to get a new job/promotion at '${companyName}' in the '${field}' field.`);
}
return res;
return jobName;
},
quitJob: (ctx) => (_companyName) => {
helpers.checkSingularityAccess(ctx);

@ -79,19 +79,6 @@ export class PlayerObject extends Person implements IPlayer {
startWork = workMethods.startWork;
processWork = workMethods.processWork;
finishWork = workMethods.finishWork;
applyForSoftwareJob = generalMethods.applyForSoftwareJob;
applyForSoftwareConsultantJob = generalMethods.applyForSoftwareConsultantJob;
applyForItJob = generalMethods.applyForItJob;
applyForSecurityEngineerJob = generalMethods.applyForSecurityEngineerJob;
applyForNetworkEngineerJob = generalMethods.applyForNetworkEngineerJob;
applyForBusinessJob = generalMethods.applyForBusinessJob;
applyForBusinessConsultantJob = generalMethods.applyForBusinessConsultantJob;
applyForSecurityJob = generalMethods.applyForSecurityJob;
applyForAgentJob = generalMethods.applyForAgentJob;
applyForEmployeeJob = generalMethods.applyForEmployeeJob;
applyForPartTimeEmployeeJob = generalMethods.applyForPartTimeEmployeeJob;
applyForWaiterJob = generalMethods.applyForWaiterJob;
applyForPartTimeWaiterJob = generalMethods.applyForPartTimeWaiterJob;
applyForJob = generalMethods.applyForJob;
canAccessBladeburner = bladeburnerMethods.canAccessBladeburner;
canAccessCorporation = corporationMethods.canAccessCorporation;

@ -20,8 +20,7 @@ import { CodingContractRewardType, ICodingContractReward } from "../../CodingCon
import { Company } from "../../Company/Company";
import { Companies } from "../../Company/Companies";
import { getNextCompanyPositionHelper } from "../../Company/GetNextCompanyPosition";
import { getJobRequirementText } from "../../Company/GetJobRequirementText";
import { CompanyPositions } from "../../Company/CompanyPositions";
import { getJobRequirements, getJobRequirementText } from "../../Company/GetJobRequirements";
import { CompanyPosition } from "../../Company/CompanyPosition";
import { CONSTANTS } from "../../Constants";
import { Exploit } from "../../Exploits/Exploit";
@ -51,7 +50,7 @@ import { SnackbarEvents } from "../../ui/React/Snackbar";
import { achievements } from "../../Achievements/Achievements";
import { isCompanyWork } from "../../Work/CompanyWork";
import { getEnumHelper, isMember } from "../../utils/EnumHelper";
import { isMember } from "../../utils/EnumHelper";
export function init(this: PlayerObject): void {
/* Initialize Player's home computer */
@ -273,26 +272,34 @@ export function hospitalize(this: PlayerObject): number {
return cost;
}
/********* Company job application **********/
//Determines the job that the Player should get (if any) at the current company
//The 'sing' argument designates whether or not this is being called from
//the applyToCompany() Netscript Singularity function
export function applyForJob(this: PlayerObject, entryPosType: CompanyPosition, sing = false): boolean {
const companyName = getEnumHelper("CompanyName").getMember(this.location);
if (!companyName) return false;
const company = Companies[companyName]; //Company being applied to
let pos = entryPosType;
/**
* Company job application. Determines the job that the Player should get (if any) at the given company.
* @param this The player instance
* @param company The company being applied to
* @param position A specific position
* @param sing Whether this is being called from the applyToCompany() Netscript Singularity function
* @returns The name of the Job received (if any). May be higher or lower than the job applied to.
*/
export function applyForJob(
this: PlayerObject,
company: Company,
position: CompanyPosition,
sing = false,
): JobName | null {
if (!company) return null;
// Start searching the job track from the provided point (which may not be the entry position)
let pos = position;
if (!this.isQualified(company, pos)) {
if (!sing) {
dialogBoxCreate("Unfortunately, you do not qualify for this position\n" + getJobRequirementText(company, pos));
dialogBoxCreate(`Unfortunately, you do not qualify for this position.\n${getJobRequirementText(company, pos)}`);
}
return false;
return null;
}
if (!company.hasPosition(pos)) {
console.error(`Company ${company.name} does not have position ${pos}. Player.applyToCompany() failed`);
return false;
console.error(`Company ${company.name} does not have position ${pos}. Player.applyToCompany() failed.`);
return null;
}
let nextPos = getNextCompanyPositionHelper(pos);
@ -305,59 +312,53 @@ export function applyForJob(this: PlayerObject, entryPosType: CompanyPosition, s
if (this.jobs[company.name] === pos.name) {
if (!sing) {
const nextPos = getNextCompanyPositionHelper(pos);
if (nextPos == null || !company.hasPosition(nextPos)) {
dialogBoxCreate("You are already at the highest position for your field! No promotion available");
if (nextPos == null) {
dialogBoxCreate(`You are already ${pos.name}! No promotion available`);
} else if (!company.hasPosition(nextPos)) {
dialogBoxCreate(
`You already have the highest ${pos.field} position available at ${company.name}! No promotion available`,
);
} else {
const reqText = getJobRequirementText(company, nextPos);
dialogBoxCreate("Unfortunately, you do not qualify for a promotion\n" + reqText);
dialogBoxCreate(
`Unfortunately, you do not qualify for a promotion.\n${getJobRequirementText(company, nextPos)}`,
);
}
}
return false;
return null;
}
this.jobs[company.name] = pos.name;
if (!sing) {
dialogBoxCreate(`Congratulations! You were offered a new job at ${company.name} for position ${pos.name}!`);
dialogBoxCreate(`${pos.hiredText} at ${company.name}!`);
}
return true;
return pos.name;
}
//Returns your next position at a company given the field (software, business, etc.)
/**
* Get a job position that the player can apply for.
* @param this The player instance
* @param company The Company being applied to
* @param entryPosType Job field (Software, Business, etc)
* @returns The highest job the player can apply for at this company, if any
*/
export function getNextCompanyPosition(
this: PlayerObject,
company: Company,
entryPosType: CompanyPosition,
): CompanyPosition | null {
const currCompany = Companies[company.name];
//Not employed at this company, so return the entry position
if (currCompany == null || currCompany.name != company.name) {
return entryPosType;
let pos: CompanyPosition | null = entryPosType;
let nextPos = getNextCompanyPositionHelper(pos);
// Find the highest-level job in this category that the player is currently able to apply for.
while (nextPos && company.hasPosition(nextPos) && this.isQualified(company, nextPos)) {
pos = nextPos;
nextPos = getNextCompanyPositionHelper(pos);
}
//If the entry pos type and the player's current position have the same type,
//return the player's "nextCompanyPosition". Otherwise return the entryposType
//Employed at this company, so just return the next position if it exists.
const currentPositionName = this.jobs[company.name];
if (!currentPositionName) return entryPosType;
const currentPosition = CompanyPositions[currentPositionName];
if (
(currentPosition.isSoftwareJob() && entryPosType.isSoftwareJob()) ||
(currentPosition.isITJob() && entryPosType.isITJob()) ||
(currentPosition.isBusinessJob() && entryPosType.isBusinessJob()) ||
(currentPosition.isSecurityEngineerJob() && entryPosType.isSecurityEngineerJob()) ||
(currentPosition.isNetworkEngineerJob() && entryPosType.isNetworkEngineerJob()) ||
(currentPosition.isSecurityJob() && entryPosType.isSecurityJob()) ||
(currentPosition.isAgentJob() && entryPosType.isAgentJob()) ||
(currentPosition.isSoftwareConsultantJob() && entryPosType.isSoftwareConsultantJob()) ||
(currentPosition.isBusinessConsultantJob() && entryPosType.isBusinessConsultantJob()) ||
(currentPosition.isPartTimeJob() && entryPosType.isPartTimeJob())
) {
return getNextCompanyPositionHelper(currentPosition);
// If the player already has this position, return the one after that (if any).
if (this.jobs[company.name] == pos.name) {
pos = nextPos;
}
return entryPosType;
return pos;
}
export function quitJob(this: PlayerObject, company: CompanyName): void {
@ -382,192 +383,10 @@ export function hasJob(this: PlayerObject): boolean {
return Boolean(Object.keys(this.jobs).length);
}
export function applyForSoftwareJob(this: PlayerObject, sing = false): boolean {
return this.applyForJob(CompanyPositions[JobName.software0], sing);
}
export function applyForSoftwareConsultantJob(this: PlayerObject, sing = false): boolean {
return this.applyForJob(CompanyPositions[JobName.softwareConsult0], sing);
}
export function applyForItJob(this: PlayerObject, sing = false): boolean {
return this.applyForJob(CompanyPositions[JobName.IT0], sing);
}
export function applyForSecurityEngineerJob(this: PlayerObject, sing = false): boolean {
const companyName = getEnumHelper("CompanyName").getMember(this.location);
if (!companyName) return false;
const company = Companies[companyName];
if (this.isQualified(company, CompanyPositions[JobName.securityEng])) {
return this.applyForJob(CompanyPositions[JobName.securityEng], sing);
} else {
if (!sing) {
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
}
export function applyForNetworkEngineerJob(this: PlayerObject, sing = false): boolean {
const companyName = getEnumHelper("CompanyName").getMember(this.location);
if (!companyName) return false;
const company = Companies[companyName];
if (this.isQualified(company, CompanyPositions[JobName.networkEng0])) {
const pos = CompanyPositions[JobName.networkEng0];
return this.applyForJob(pos, sing);
} else {
if (!sing) {
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
}
export function applyForBusinessJob(this: PlayerObject, sing = false): boolean {
return this.applyForJob(CompanyPositions[JobName.business0], sing);
}
export function applyForBusinessConsultantJob(this: PlayerObject, sing = false): boolean {
return this.applyForJob(CompanyPositions[JobName.businessConsult0], sing);
}
export function applyForSecurityJob(this: PlayerObject, sing = false): boolean {
// TODO Police Jobs
// Indexing starts at 2 because 0 is for police officer
return this.applyForJob(CompanyPositions[JobName.security0], sing);
}
export function applyForAgentJob(this: PlayerObject, sing = false): boolean {
const companyName = getEnumHelper("CompanyName").getMember(this.location);
if (!companyName) return false;
const company = Companies[companyName];
if (this.isQualified(company, CompanyPositions[JobName.agent0])) {
const pos = CompanyPositions[JobName.agent0];
return this.applyForJob(pos, sing);
} else {
if (!sing) {
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
}
export function applyForEmployeeJob(this: PlayerObject, sing = false): boolean {
const companyName = getEnumHelper("CompanyName").getMember(this.location);
if (!companyName) return false;
const company = Companies[companyName];
const position = JobName.employee;
// Check if this company has the position
if (!company.hasPosition(position)) {
return false;
}
if (this.isQualified(company, CompanyPositions[position])) {
this.jobs[company.name] = position;
if (!sing) {
dialogBoxCreate("Congratulations, you are now employed at " + this.location);
}
return true;
} else {
if (!sing) {
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
}
export function applyForPartTimeEmployeeJob(this: PlayerObject, sing = false): boolean {
const companyName = getEnumHelper("CompanyName").getMember(this.location);
if (!companyName) return false;
const company = Companies[companyName];
const position = JobName.employeePT;
// Check if this company has the position
if (!company.hasPosition(position)) {
return false;
}
if (this.isQualified(company, CompanyPositions[position])) {
this.jobs[company.name] = position;
if (!sing) {
dialogBoxCreate("Congratulations, you are now employed part-time at " + this.location);
}
return true;
} else {
if (!sing) {
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
}
export function applyForWaiterJob(this: PlayerObject, sing = false): boolean {
const companyName = getEnumHelper("CompanyName").getMember(this.location);
if (!companyName) return false;
const company = Companies[companyName];
const position = JobName.waiter;
// Check if this company has the position
if (!company.hasPosition(position)) {
return false;
}
if (this.isQualified(company, CompanyPositions[position])) {
this.jobs[company.name] = position;
if (!sing) {
dialogBoxCreate("Congratulations, you are now employed as a waiter at " + this.location);
}
return true;
} else {
if (!sing) {
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
}
export function applyForPartTimeWaiterJob(this: PlayerObject, sing = false): boolean {
const companyName = getEnumHelper("CompanyName").getMember(this.location);
if (!companyName) return false;
const company = Companies[companyName];
const position = JobName.waiterPT;
// Check if this company has the position
if (!company.hasPosition(position)) {
return false;
}
if (this.isQualified(company, CompanyPositions[position])) {
this.jobs[company.name] = position;
if (!sing) {
dialogBoxCreate("Congratulations, you are now employed as a part-time waiter at " + this.location);
}
return true;
} else {
if (!sing) {
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
}
//Checks if the Player is qualified for a certain position
export function isQualified(this: PlayerObject, company: Company, position: CompanyPosition): boolean {
const offset = company.jobStatReqOffset;
const reqHacking = position.requiredHacking > 0 ? position.requiredHacking + offset : 0;
const reqStrength = position.requiredStrength > 0 ? position.requiredStrength + offset : 0;
const reqDefense = position.requiredDefense > 0 ? position.requiredDefense + offset : 0;
const reqDexterity = position.requiredDexterity > 0 ? position.requiredDexterity + offset : 0;
const reqAgility = position.requiredDexterity > 0 ? position.requiredDexterity + offset : 0;
const reqCharisma = position.requiredCharisma > 0 ? position.requiredCharisma + offset : 0;
return (
this.skills.hacking >= reqHacking &&
this.skills.strength >= reqStrength &&
this.skills.defense >= reqDefense &&
this.skills.dexterity >= reqDexterity &&
this.skills.agility >= reqAgility &&
this.skills.charisma >= reqCharisma &&
company.playerReputation >= position.requiredReputation
);
const reqs = getJobRequirements(company, position);
return reqs.every((req) => req.isSatisfied(this));
}
/********** Reapplying Augmentations and Source File ***********/

@ -1893,7 +1893,7 @@ export interface Singularity {
* @param field - Field to which you want to apply.
* @returns True if the player successfully get a job/promotion, and false otherwise.
*/
applyToCompany(companyName: CompanyName | `${CompanyName}`, field: JobField | `${JobField}`): boolean;
applyToCompany(companyName: CompanyName | `${CompanyName}`, field: JobField | `${JobField}`): JobName | null;
/**
* Get company reputation.

@ -3,14 +3,13 @@ import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue
import { Player } from "@player";
import { Work, WorkType } from "./Work";
import { influenceStockThroughCompanyWork } from "../StockMarket/PlayerInfluencing";
import { AugmentationName, CompanyName, JobName } from "@enums";
import { CompanyName, JobName } from "@enums";
import { calculateCompanyWorkStats } from "./Formulas";
import { Companies } from "../Company/Companies";
import { applyWorkStats, scaleWorkStats, WorkStats } from "./WorkStats";
import { Company } from "../Company/Company";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reputation } from "../ui/React/Reputation";
import { CONSTANTS } from "../Constants";
import { CompanyPositions } from "../Company/CompanyPositions";
import { isMember } from "../utils/EnumHelper";
import { invalidWork } from "./InvalidWork";
@ -34,10 +33,7 @@ export class CompanyWork extends Work {
}
getGainRates(job: JobName): WorkStats {
let focusBonus = 1;
if (!Player.hasAugmentation(AugmentationName.NeuroreceptorManager, true)) {
focusBonus = Player.focus ? 1 : CONSTANTS.BaseFocusBonus;
}
const focusBonus = CompanyPositions[job].isPartTime ? 1 : Player.focusPenalty();
const company = this.getCompany();
return scaleWorkStats(calculateCompanyWorkStats(Player, company, CompanyPositions[job], company.favor), focusBonus);
}

@ -1,6 +1,6 @@
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { AugmentationName, CompletedProgramName } from "@enums";
import { CompletedProgramName } from "@enums";
import { CONSTANTS } from "../Constants";
import { Player } from "@player";
import { Programs } from "../Programs/Programs";
@ -55,10 +55,7 @@ export class CreateProgramWork extends Work {
}
process(cycles: number): boolean {
let focusBonus = 1;
if (!Player.hasAugmentation(AugmentationName.NeuroreceptorManager, true)) {
focusBonus = Player.focus ? 1 : CONSTANTS.BaseFocusBonus;
}
const focusBonus = Player.focusPenalty();
//Higher hacking skill will allow you to create programs faster
const reqLvl = this.getProgram().create?.level ?? 0;
let skillMult = (Player.skills.hacking / reqLvl) * calculateIntelligenceBonus(Player.skills.intelligence, 3); //This should always be greater than 1;

@ -55,10 +55,10 @@ export class CrimeWork extends Work {
);
return;
}
const focusPenalty = Player.focusPenalty();
const focusBonus = Player.focusPenalty();
// exp times 2 because were trying to maintain the same numbers as before the conversion
// Technically the definition of Crimes should have the success numbers and failure should divide by 4
let gains = scaleWorkStats(this.earnings(), focusPenalty, false);
let gains = scaleWorkStats(this.earnings(), focusBonus, false);
let karma = crime.karma;
const success = determineCrimeSuccess(crime.type);
if (success) {
@ -75,7 +75,7 @@ export class CrimeWork extends Work {
Player.gainDexterityExp(gains.dexExp);
Player.gainAgilityExp(gains.agiExp);
Player.gainCharismaExp(gains.chaExp);
Player.karma -= karma * focusPenalty;
Player.karma -= karma * focusBonus;
}
finish(): void {

@ -4,12 +4,11 @@ import React from "react";
import { Work, WorkType } from "./Work";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { Player } from "@player";
import { AugmentationName, FactionName, FactionWorkType } from "@enums";
import { FactionName, FactionWorkType } from "@enums";
import { Factions } from "../Faction/Factions";
import { applyWorkStats, scaleWorkStats, WorkStats } from "./WorkStats";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reputation } from "../ui/React/Reputation";
import { CONSTANTS } from "../Constants";
import { calculateFactionExp, calculateFactionRep } from "./Formulas";
import { getEnumHelper } from "../utils/EnumHelper";
@ -36,18 +35,12 @@ export class FactionWork extends Work {
}
getReputationRate(): number {
let focusBonus = 1;
if (!Player.hasAugmentation(AugmentationName.NeuroreceptorManager, true)) {
focusBonus = Player.focus ? 1 : CONSTANTS.BaseFocusBonus;
}
const focusBonus = Player.focusPenalty();
return calculateFactionRep(Player, this.factionWorkType, this.getFaction().favor) * focusBonus;
}
getExpRates(): WorkStats {
let focusBonus = 1;
if (!Player.hasAugmentation(AugmentationName.NeuroreceptorManager, true)) {
focusBonus = Player.focus ? 1 : CONSTANTS.BaseFocusBonus;
}
const focusBonus = Player.focusPenalty();
const rate = calculateFactionExp(Player, this.factionWorkType);
return scaleWorkStats(rate, focusBonus, false);
}

@ -35,11 +35,7 @@ export class GraftingWork extends Work {
}
process(cycles: number): boolean {
let focusBonus = 1;
if (!Player.hasAugmentation(AugmentationName.NeuroreceptorManager, true)) {
focusBonus = Player.focus ? 1 : CONSTANTS.BaseFocusBonus;
}
const focusBonus = Player.focusPenalty();
this.cyclesWorked += cycles;
this.unitCompleted += CONSTANTS.MilliPerCycle * cycles * graftingIntBonus() * focusBonus;