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,25 +1,42 @@
import { JobName } from "@enums";
export const softwareJobs = [
JobName.software0,
JobName.software1,
JobName.software2,
JobName.software3,
JobName.software4,
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 = [
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];
import { JobName, JobField } from "@enums";
export const JobTracks: Record<JobField, readonly JobName[]> = {
[JobField.software]: [
JobName.software0,
JobName.software1,
JobName.software2,
JobName.software3,
JobName.software4,
JobName.software5,
JobName.software6,
JobName.software7,
],
[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,
],
[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,59 +97,58 @@ 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 (
<>
{isEmployedHere && hasMoreJobs && (
<>
<Box>
<Button onClick={() => switchLoc(-1)}>Previous</Button>
<Button onClick={() => switchLoc(1)}>Next</Button>
<Box sx={{ display: "grid", width: "fit-content", minWidth: "25em" }}>
{isEmployedHere && hasMoreJobs && (
<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">
<Tooltip
title={
<>
You will have <Favor favor={company.favor + favorGain} /> company favor upon resetting after
installing Augmentations
</>
}
>
<Typography>
Company reputation: <Reputation reputation={company.playerReputation} />
</Typography>
</Tooltip>
</Box>
<Typography>-------------------------</Typography>
<Box display="flex">
<Tooltip
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.
</>
}
>
<Typography>
Company Favor: <Favor favor={company.favor} />
</Typography>
</Tooltip>
</Box>
<Typography>-------------------------</Typography>
<br />
</>
)}
<Box sx={{ display: "grid", width: "fit-content" }}>
)}
{isEmployedHere && (
<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
installing Augmentations
</>
}
>
<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.
</>
}
>
<Typography>Company favor:</Typography>
</Tooltip>,
<Favor key="favor" favor={company.favor} />,
],
]}
/>
</Paper>
)}
{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;