diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts index 1ec820037..d3b6b31c9 100644 --- a/src/Corporation/Actions.ts +++ b/src/Corporation/Actions.ts @@ -304,9 +304,19 @@ export function BuyBackShares(corporation: ICorporation, player: IPlayer, numSha return true; } -export function AssignJob(employee: Employee, job: string): void { +export function AssignJob(office: OfficeSpace, employeeName: string, job: string): void { + const employee = office.employees.find(e => e.name === employeeName); + + if (!employee) throw new Error(`Could not find employee '${name}'.`); if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`); - employee.pos = job; + + office.assignSingleJob(employee, job); +} + +export function AutoAssignJob(office: OfficeSpace, job: string, count: number): boolean { + if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`); + + return office.autoAssignJob(job, count); } export function UpgradeOfficeSize(corp: ICorporation, office: OfficeSpace, size: number): void { diff --git a/src/Corporation/Employee.ts b/src/Corporation/Employee.ts index 60a08f202..17b485e25 100644 --- a/src/Corporation/Employee.ts +++ b/src/Corporation/Employee.ts @@ -34,6 +34,7 @@ export class Employee { cyclesUntilRaise = CorporationConstants.CyclesPerEmployeeRaise; loc: string; pos: string; + nextPos: string; constructor(params: IParams = {}) { this.name = params.name ? params.name : "Bobby"; @@ -52,6 +53,7 @@ export class Employee { this.loc = params.loc ? params.loc : ""; this.pos = EmployeePositions.Unassigned; + this.nextPos = this.pos; } //Returns the amount the employee needs to be paid diff --git a/src/Corporation/OfficeSpace.ts b/src/Corporation/OfficeSpace.ts index 623b3c5d3..fe5f2d781 100644 --- a/src/Corporation/OfficeSpace.ts +++ b/src/Corporation/OfficeSpace.ts @@ -37,7 +37,15 @@ export class OfficeSpace { [EmployeePositions.RandD]: 0, [EmployeePositions.Training]: 0, [EmployeePositions.Unassigned]: 0, - total: 0, + }; + employeeNextJobs: { [key: string]: number } = { + [EmployeePositions.Operations]: 0, + [EmployeePositions.Engineer]: 0, + [EmployeePositions.Business]: 0, + [EmployeePositions.Management]: 0, + [EmployeePositions.RandD]: 0, + [EmployeePositions.Training]: 0, + [EmployeePositions.Unassigned]: 0, }; constructor(params: IParams = {}) { @@ -58,7 +66,13 @@ export class OfficeSpace { } } + for (let i = 0; i < this.employees.length; ++i) { + const emp = this.employees[i]; + emp.pos = emp.nextPos; + } + this.calculateTotalEmployees(); + this.calculateNextEmployees(); // Process Office properties this.maxEne = 100; @@ -113,6 +127,18 @@ export class OfficeSpace { return salaryPaid; } + calculateNextEmployees(): void { + //Reset + for (const name of Object.keys(this.employeeNextJobs)) { + this.employeeNextJobs[name] = 0; + } + + for (let i = 0; i < this.employees.length; ++i) { + const employee = this.employees[i]; + this.employeeNextJobs[employee.nextPos]++; + } + } + calculateTotalEmployees(): void { //Reset for (const name of Object.keys(this.employeeJobs)) { @@ -123,7 +149,6 @@ export class OfficeSpace { const employee = this.employees[i]; this.employeeJobs[employee.pos]++; } - this.employeeJobs.total = this.employees.length; } calculateEmployeeProductivity(corporation: ICorporation, industry: IIndustry): void { @@ -173,45 +198,33 @@ export class OfficeSpace { emp.name = name; this.employees.push(emp); + this.calculateTotalEmployees(); + this.calculateNextEmployees(); return emp; } - //Finds the first unassigned employee and assigns its to the specified job - assignEmployeeToJob(job: string): boolean { - for (let i = 0; i < this.employees.length; ++i) { - if (this.employees[i].pos === EmployeePositions.Unassigned) { - this.employees[i].pos = job; - return true; - } - } - return false; + assignSingleJob(employee: Employee, job: string): void { + employee.nextPos = job; + this.calculateNextEmployees(); } - //Finds the first employee with the given job and unassigns it - unassignEmployeeFromJob(job: string): boolean { - for (let i = 0; i < this.employees.length; ++i) { - if (this.employees[i].pos === job) { - this.employees[i].pos = EmployeePositions.Unassigned; - return true; - } - } - return false; - } - - setEmployeeToJob(job: string, amount: number): boolean { - let jobCount = this.employees.reduce((acc, employee) => (employee.pos === job ? acc + 1 : acc), 0); + autoAssignJob(job: string, target: number): boolean { + let count = this.employeeNextJobs[job]; for (const employee of this.employees) { - if (jobCount == amount) return true; - if (employee.pos === EmployeePositions.Unassigned && jobCount <= amount) { - employee.pos = job; - jobCount++; - } else if (employee.pos === job && jobCount >= amount) { - employee.pos = EmployeePositions.Unassigned; - jobCount--; + if (count === target) { + break; + } else if (employee.nextPos === EmployeePositions.Unassigned && count <= target) { + employee.nextPos = job; + count++; + } else if (employee.nextPos === job && count >= target) { + employee.nextPos = EmployeePositions.Unassigned; + count--; } } - return jobCount === amount; + + this.calculateNextEmployees(); + return count === target; } toJSON(): any { diff --git a/src/Corporation/ui/IndustryOffice.tsx b/src/Corporation/ui/IndustryOffice.tsx index 10bf65af8..a4536de28 100644 --- a/src/Corporation/ui/IndustryOffice.tsx +++ b/src/Corporation/ui/IndustryOffice.tsx @@ -115,14 +115,14 @@ function ManualManagement(props: IProps): React.ReactElement { {positionNames[i]} , ); - if (emp != null && emp.pos === positionNames[i]) { - employeePositionSelectorInitialValue = positionNames[i]; + if (emp != null && emp.nextPos === positionNames[i]) { + employeePositionSelectorInitialValue = emp.nextPos; } } function employeePositionSelectorOnChange(e: SelectChangeEvent): void { if (employee === null) return; - employee.pos = e.target.value; + props.office.assignSingleJob(employee, e.target.value); props.rerender(); } @@ -181,38 +181,40 @@ interface IAutoAssignProps { function AutoAssignJob(props: IAutoAssignProps): React.ReactElement { const corp = useCorporation(); const division = useDivision(); - const numJob = countEmployee(props.office.employees, props.job); - const numUnassigned = countEmployee(props.office.employees, EmployeePositions.Unassigned); + + const currJob = props.office.employeeJobs[props.job]; + const nextJob = props.office.employeeNextJobs[props.job]; + const nextUna = props.office.employeeNextJobs[EmployeePositions.Unassigned]; + function assignEmployee(): void { - if (numUnassigned <= 0) { + if (nextUna <= 0) { console.warn("Cannot assign employee. No unassigned employees available"); return; } - props.office.assignEmployeeToJob(props.job); - props.office.calculateEmployeeProductivity(corp, division); + props.office.autoAssignJob(props.job, nextJob + 1); props.rerender(); } function unassignEmployee(): void { - props.office.unassignEmployeeFromJob(props.job); - props.office.calculateEmployeeProductivity(corp, division); + props.office.autoAssignJob(props.job, nextJob - 1); props.rerender(); } + return ( - {props.job} ({numJob}) + {props.job} ({(currJob == nextJob ? currJob : `${currJob} -> ${nextJob}`)}) - + - + @@ -223,7 +225,6 @@ function AutoAssignJob(props: IAutoAssignProps): React.ReactElement { function AutoManagement(props: IProps): React.ReactElement { const corp = useCorporation(); const division = useDivision(); - const numUnassigned = countEmployee(props.office.employees, EmployeePositions.Unassigned); const vechain = corp.unlockUpgrades[4] === 1; // Has Vechain upgrade // Calculate average morale, happiness, energy, and salary. @@ -247,6 +248,9 @@ function AutoManagement(props: IProps): React.ReactElement { avgEnergy = totalEnergy / props.office.employees.length; } + const currUna = props.office.employeeJobs[EmployeePositions.Unassigned]; + const nextUna = props.office.employeeNextJobs[EmployeePositions.Unassigned]; + return ( <> @@ -256,7 +260,7 @@ function AutoManagement(props: IProps): React.ReactElement { Unassigned Employees: - {numUnassigned} + {(currUna == nextUna ? currUna : `${currUna} -> ${nextUna}`)} diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts index 9ed771625..27f9ab7a9 100644 --- a/src/NetscriptFunctions/Corporation.ts +++ b/src/NetscriptFunctions/Corporation.ts @@ -34,6 +34,7 @@ import { SetSmartSupply, BuyMaterial, AssignJob, + AutoAssignJob, UpgradeOfficeSize, ThrowParty, PurchaseWarehouse, @@ -685,20 +686,6 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript const researchName = ctx.helper.string("researchName", _researchName); return hasResearched(getDivision(divisionName), researchName); }, - setAutoJobAssignment: - (ctx: NetscriptContext) => - (_divisionName: unknown, _cityName: unknown, _job: unknown, _amount: unknown): Promise => { - checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const amount = ctx.helper.number("amount", _amount); - const job = ctx.helper.string("job", _job); - const office = getOffice(divisionName, cityName); - if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`); - return netscriptDelay(1000, workerScript).then(function () { - return Promise.resolve(office.setEmployeeToJob(job, amount)); - }); - }, getOfficeSizeUpgradeCost: (ctx: NetscriptContext) => (_divisionName: unknown, _cityName: unknown, _size: unknown): number => { @@ -724,10 +711,31 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript const cityName = ctx.helper.city("cityName", _cityName); const employeeName = ctx.helper.string("employeeName", _employeeName); const job = ctx.helper.string("job", _job); - const employee = getEmployee(divisionName, cityName, employeeName); - return netscriptDelay(["Training", "Unassigned"].includes(employee.pos) ? 0 : 1000, workerScript).then(function () { - return Promise.resolve(AssignJob(employee, job)); + + if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`); + const office = getOffice(divisionName, cityName); + + return netscriptDelay(0, workerScript).then(function () { + return Promise.resolve(AssignJob(office, employeeName, job)); }); + //return AssignJob(office, employeeName, job); + }, + setAutoJobAssignment: + (ctx: NetscriptContext) => + (_divisionName: unknown, _cityName: unknown, _job: unknown, _amount: unknown): Promise => { + checkAccess(ctx, 8); + const divisionName = ctx.helper.string("divisionName", _divisionName); + const cityName = ctx.helper.city("cityName", _cityName); + const amount = ctx.helper.number("amount", _amount); + const job = ctx.helper.string("job", _job); + + if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`); + const office = getOffice(divisionName, cityName); + + return netscriptDelay(0, workerScript).then(function () { + return Promise.resolve(AutoAssignJob(office, job, amount)); + }); + //return AutoAssignJob(office, job, amount); }, hireEmployee: (ctx: NetscriptContext) =>