diff --git a/src/Corporation/Corporation.jsx b/src/Corporation/Corporation.jsx index 02f4c9a76..681960e18 100644 --- a/src/Corporation/Corporation.jsx +++ b/src/Corporation/Corporation.jsx @@ -12,6 +12,8 @@ import { MaterialSizes } from "./MaterialSizes"; import { Product } from "./Product"; import { ResearchMap } from "./ResearchMap"; import { Warehouse } from "./Warehouse"; +import { Employee } from "./Employee"; +import { OfficeSpace } from "./OfficeSpace"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { showLiterature } from "../Literature/LiteratureHelpers"; @@ -1453,167 +1455,6 @@ Industry.fromJSON = function(value) { Reviver.constructors.Industry = Industry; -function Employee(params={}) { - if (!(this instanceof Employee)) { - return new Employee(params); - } - this.name = params.name ? params.name : "Bobby"; - - //Morale, happiness, and energy are 0-100 - this.mor = params.morale ? params.morale : getRandomInt(50, 100); - this.hap = params.happiness ? params.happiness : getRandomInt(50, 100); - this.ene = params.energy ? params.energy : getRandomInt(50, 100); - - this.int = params.intelligence ? params.intelligence : getRandomInt(10, 50); - this.cha = params.charisma ? params.charisma : getRandomInt(10, 50); - this.exp = params.experience ? params.experience : getRandomInt(10, 50); - this.cre = params.creativity ? params.creativity : getRandomInt(10, 50); - this.eff = params.efficiency ? params.efficiency : getRandomInt(10, 50); - this.sal = params.salary ? params.salary : getRandomInt(0.1, 5); - this.pro = 0; //Productivity, This is calculated - - this.cyclesUntilRaise = CyclesPerEmployeeRaise; - - this.loc = params.loc ? params.loc : ""; - this.pos = EmployeePositions.Unassigned; -} - -//Returns the amount the employee needs to be paid -Employee.prototype.process = function(marketCycles=1, office) { - var gain = 0.003 * marketCycles, - det = gain * Math.random(); - this.exp += gain; - - // Employee salaries slowly go up over time - this.cyclesUntilRaise -= marketCycles; - if (this.cyclesUntilRaise <= 0) { - this.salary += EmployeeRaiseAmount; - this.cyclesUntilRaise += CyclesPerEmployeeRaise; - } - - //Training - var trainingEff = gain * Math.random(); - if (this.pos === EmployeePositions.Training) { - //To increase creativity and intelligence special upgrades are needed - this.cha += trainingEff; - this.exp += trainingEff; - this.eff += trainingEff; - } - - this.ene -= det; - this.hap -= det; - - if (this.ene < office.minEne) {this.ene = office.minEne;} - if (this.hap < office.minHap) {this.hap = office.minHap;} - var salary = this.sal * marketCycles * SecsPerMarketCycle; - return salary; -} - -Employee.prototype.calculateProductivity = function(corporation, industry) { - var effCre = this.cre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(), - effCha = this.cha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(), - effInt = this.int * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(), - effEff = this.eff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier(); - const prodBase = this.mor * this.hap * this.ene * 1e-6; - let prodMult; - switch(this.pos) { - //Calculate productivity based on position. This is multipled by prodBase - //to get final value - case EmployeePositions.Operations: - prodMult = (0.6 * effInt) + (0.1 * effCha) + (this.exp) + - (0.5 * effCre) + (effEff); - break; - case EmployeePositions.Engineer: - prodMult = (effInt) + (0.1 * effCha) + (1.5 * this.exp) + - (effEff); - break; - case EmployeePositions.Business: - prodMult = (0.4 * effInt) + (effCha) + (0.5 * this.exp); - break; - case EmployeePositions.Management: - prodMult = (2 * effCha) + (this.exp) + (0.2 * effCre) + - (0.7 * effEff); - break; - case EmployeePositions.RandD: - prodMult = (1.5 * effInt) + (0.8 * this.exp) + (effCre) + - (0.5 * effEff); - break; - case EmployeePositions.Unassigned: - case EmployeePositions.Training: - prodMult = 0; - break; - default: - console.error(`Invalid employee position: ${this.pos}`); - break; - } - return prodBase * prodMult; -} - -//Process benefits from having an office party thrown -Employee.prototype.throwParty = function(money) { - var mult = 1 + (money / 10e6); - this.mor *= mult; - this.mor = Math.min(100, this.mor); - this.hap *= mult; - this.hap = Math.min(100, this.hap); - return mult; -} - -//'panel' is the DOM element on which to create the UI -Employee.prototype.createUI = function(panel, corporation, industry) { - var effCre = this.cre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(), - effCha = this.cha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(), - effInt = this.int * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(), - effEff = this.eff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier(); - panel.style.color = "white"; - panel.appendChild(createElement("p", { - id:"cmpy-mgmt-employee-" + this.name + "-panel-text", - innerHTML:"Morale: " + formatNumber(this.mor, 3) + "
" + - "Happiness: " + formatNumber(this.hap, 3) + "
" + - "Energy: " + formatNumber(this.ene, 3) + "
" + - "Intelligence: " + formatNumber(effInt, 3) + "
" + - "Charisma: " + formatNumber(effCha, 3) + "
" + - "Experience: " + formatNumber(this.exp, 3) + "
" + - "Creativity: " + formatNumber(effCre, 3) + "
" + - "Efficiency: " + formatNumber(effEff, 3) + "
" + - "Salary: " + numeralWrapper.format(this.sal, "$0.000a") + "/ s
", - })); - - //Selector for employee position - var selector = createElement("select", {}); - for (var key in EmployeePositions) { - if (EmployeePositions.hasOwnProperty(key)) { - selector.add(createElement("option", { - text: EmployeePositions[key], - value: EmployeePositions[key], - })); - } - } - - selector.addEventListener("change", () => { - this.pos = selector.options[selector.selectedIndex].value; - }); - - //Set initial value of selector - for (var i = 0; i < selector.length; ++i) { - if (selector.options[i].value === this.pos) { - selector.selectedIndex = i; - break; - } - } - panel.appendChild(selector); -} - -Employee.prototype.toJSON = function() { - return Generic_toJSON("Employee", this); -} - -Employee.fromJSON = function(value) { - return Generic_fromJSON(Employee, value.data); -} - -Reviver.constructors.Employee = Employee; - var OfficeSpaceTiers = { Basic: "Basic", Enhanced: "Enhanced", @@ -1621,298 +1462,6 @@ var OfficeSpaceTiers = { Extravagant: "Extravagant", } -function OfficeSpace(params={}) { - this.loc = params.loc ? params.loc : ""; - this.cost = params.cost ? params.cost : 1; - this.size = params.size ? params.size : 1; - this.comf = params.comfort ? params.comfort : 1; - this.beau = params.beauty ? params.beauty : 1; - this.tier = OfficeSpaceTiers.Basic; - - // Min/max energy of employees - this.minEne = 0; - this.maxEne = 100; - - // Min/max Happiness of office - this.minHap = 0; - this.maxHap = 100; - - // Maximum Morale of office - this.maxMor = 100; - - this.employees = []; - this.employeeProd = { - [EmployeePositions.Operations]: 0, - [EmployeePositions.Engineer]: 0, - [EmployeePositions.Business]: 0, - [EmployeePositions.Management]: 0, - [EmployeePositions.RandD]: 0, - total: 0, - }; -} - -OfficeSpace.prototype.atCapacity = function() { - return (this.employees.length) >= this.size; -} - -OfficeSpace.prototype.process = function(marketCycles=1, parentRefs) { - var industry = parentRefs.industry; - - // HRBuddy AutoRecruitment and training - if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) { - const emp = this.hireRandomEmployee(); - if (industry.hasResearch("HRBuddy-Training")) { - emp.pos = EmployeePositions.Training; - } - } - - // Process Office properties - this.maxEne = 100; - this.maxHap = 100; - this.maxMor = 100; - if (industry.hasResearch("Go-Juice")) { - this.maxEne += 10; - } - if (industry.hasResearch("JoyWire")) { - this.maxHap += 10; - } - if (industry.hasResearch("Sti.mu")) { - this.maxMor += 10; - } - - // Calculate changes in Morale/Happiness/Energy for Employees - var perfMult=1; //Multiplier for employee morale/happiness/energy based on company performance - if (industry.funds < 0 && industry.lastCycleRevenue < 0) { - perfMult = Math.pow(0.99, marketCycles); - } else if (industry.funds > 0 && industry.lastCycleRevenue > 0) { - perfMult = Math.pow(1.01, marketCycles); - } - - const hasAutobrew = industry.hasResearch("AutoBrew"); - const hasAutoparty = industry.hasResearch("AutoPartyManager"); - - var salaryPaid = 0; - for (let i = 0; i < this.employees.length; ++i) { - const emp = this.employees[i]; - if (hasAutoparty) { - emp.mor = this.maxMor; - emp.hap = this.maxHap; - } else { - emp.mor *= perfMult; - emp.hap *= perfMult; - emp.mor = Math.min(emp.mor, this.maxMor); - emp.hap = Math.min(emp.hap, this.maxHap); - } - - if (hasAutobrew) { - emp.ene = this.maxEne; - } else { - emp.ene *= perfMult; - emp.ene = Math.min(emp.ene, this.maxEne); - } - - const salary = emp.process(marketCycles, this); - salaryPaid += salary; - } - - this.calculateEmployeeProductivity(parentRefs); - return salaryPaid; -} - -OfficeSpace.prototype.calculateEmployeeProductivity = function(parentRefs) { - var company = parentRefs.corporation, industry = parentRefs.industry; - - //Reset - for (const name in this.employeeProd) { - this.employeeProd[name] = 0; - } - - var total = 0; - for (let i = 0; i < this.employees.length; ++i) { - const employee = this.employees[i]; - const prod = employee.calculateProductivity(company, industry); - this.employeeProd[employee.pos] += prod; - total += prod; - } - this.employeeProd["total"] = total; -} - -//Takes care of UI as well -OfficeSpace.prototype.findEmployees = function(parentRefs) { - if (this.atCapacity()) { return; } - if (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) {return;} - - //Generate three random employees (meh, decent, amazing) - var mult1 = getRandomInt(25, 50)/100, - mult2 = getRandomInt(51, 75)/100, - mult3 = getRandomInt(76, 100)/100; - var int = getRandomInt(50, 100), - cha = getRandomInt(50, 100), - exp = getRandomInt(50, 100), - cre = getRandomInt(50, 100), - eff = getRandomInt(50, 100), - sal = EmployeeSalaryMultiplier * (int + cha + exp + cre + eff); - - var emp1 = new Employee({ - intelligence: int * mult1, - charisma: cha * mult1, - experience: exp * mult1, - creativity: cre * mult1, - efficiency: eff * mult1, - salary: sal * mult1, - }); - - var emp2 = new Employee({ - intelligence: int * mult2, - charisma: cha * mult2, - experience: exp * mult2, - creativity: cre * mult2, - efficiency: eff * mult2, - salary: sal * mult2, - }); - - var emp3 = new Employee({ - intelligence: int * mult3, - charisma: cha * mult3, - experience: exp * mult3, - creativity: cre * mult3, - efficiency: eff * mult3, - salary: sal * mult3, - }); - - var text = createElement("h1", { - innerHTML: "Select one of the following candidates for hire:", - }); - - var createEmpDiv = function(employee, office) { - var div = createElement("div", { - class:"cmpy-mgmt-find-employee-option", - innerHTML: "Intelligence: " + formatNumber(employee.int, 1) + "
" + - "Charisma: " + formatNumber(employee.cha, 1) + "
" + - "Experience: " + formatNumber(employee.exp, 1) + "
" + - "Creativity: " + formatNumber(employee.cre, 1) + "
" + - "Efficiency: " + formatNumber(employee.eff, 1) + "
" + - "Salary: " + numeralWrapper.format(employee.sal, '$0.000a') + " \ s
", - clickListener:() => { - office.hireEmployee(employee, parentRefs); - removeElementById("cmpy-mgmt-hire-employee-popup"); - return false; - }, - }); - return div; - }; - - var cancelBtn = createElement("a", { - class:"a-link-button", - innerText:"Cancel", - float:"right", - clickListener:() => { - removeElementById("cmpy-mgmt-hire-employee-popup"); - return false; - }, - }); - - var elems = [text, - createEmpDiv(emp1, this), - createEmpDiv(emp2, this), - createEmpDiv(emp3, this), - cancelBtn]; - - createPopup("cmpy-mgmt-hire-employee-popup", elems); -} - -OfficeSpace.prototype.hireEmployee = function(employee, parentRefs) { - var company = parentRefs.corporation; - var yesBtn = yesNoTxtInpBoxGetYesButton(), - noBtn = yesNoTxtInpBoxGetNoButton(); - yesBtn.innerHTML = "Hire"; - noBtn.innerHTML = "Cancel"; - yesBtn.addEventListener("click", () => { - var name = yesNoTxtInpBoxGetInput(); - for (var i = 0; i < this.employees.length; ++i) { - if (this.employees[i].name === name) { - dialogBoxCreate("You already have an employee with this nickname! Please give every employee a unique nickname."); - return false; - } - } - employee.name = name; - this.employees.push(employee); - company.rerender(); - return yesNoTxtInpBoxClose(); - }); - noBtn.addEventListener("click", () => { - return yesNoTxtInpBoxClose(); - }); - yesNoTxtInpBoxCreate("Give your employee a nickname!"); -} - -OfficeSpace.prototype.hireRandomEmployee = function() { - if (this.atCapacity()) { return; } - if (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) {return;} - - //Generate three random employees (meh, decent, amazing) - var mult = getRandomInt(76, 100)/100; - var int = getRandomInt(50, 100), - cha = getRandomInt(50, 100), - exp = getRandomInt(50, 100), - cre = getRandomInt(50, 100), - eff = getRandomInt(50, 100), - sal = EmployeeSalaryMultiplier * (int + cha + exp + cre + eff); - - var emp = new Employee({ - intelligence: int * mult, - charisma: cha * mult, - experience: exp * mult, - creativity: cre * mult, - efficiency: eff * mult, - salary: sal * mult, - }); - - var name = generateRandomString(7); - - for (let i = 0; i < this.employees.length; ++i) { - if (this.employees[i].name === name) { - return this.hireRandomEmployee(); - } - } - emp.name = name; - this.employees.push(emp); - - return emp; -} - -//Finds the first unassigned employee and assigns its to the specified job -OfficeSpace.prototype.assignEmployeeToJob = function(job) { - for (var i = 0; i < this.employees.length; ++i) { - if (this.employees[i].pos === EmployeePositions.Unassigned) { - this.employees[i].pos = job; - return true; - } - } - return false; -} - -//Finds the first employee with the given job and unassigns it -OfficeSpace.prototype.unassignEmployeeFromJob = function(job) { - for (var i = 0; i < this.employees.length; ++i) { - if (this.employees[i].pos === job) { - this.employees[i].pos = EmployeePositions.Unassigned; - return true; - } - } - return false; -} - -OfficeSpace.prototype.toJSON = function() { - return Generic_toJSON("OfficeSpace", this); -} - -OfficeSpace.fromJSON = function(value) { - return Generic_fromJSON(OfficeSpace, value.data); -} - -Reviver.constructors.OfficeSpace = OfficeSpace; - function Corporation(params={}) { this.name = params.name ? params.name : "The Corporation"; @@ -2398,4 +1947,4 @@ Corporation.fromJSON = function(value) { Reviver.constructors.Corporation = Corporation; -export {Corporation, Industry, OfficeSpace, Warehouse}; +export {Corporation, Industry, Warehouse}; diff --git a/src/Corporation/Employee.ts b/src/Corporation/Employee.ts new file mode 100644 index 000000000..c42d2cf58 --- /dev/null +++ b/src/Corporation/Employee.ts @@ -0,0 +1,194 @@ +import { CorporationConstants } from "./data/Constants"; +import { getRandomInt } from "../../utils/helpers/getRandomInt"; +import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver"; +import { createElement } from "../../utils/uiHelpers/createElement"; +import { EmployeePositions } from "./EmployeePositions"; +import { numeralWrapper } from "../ui/numeralFormat"; +import { formatNumber } from "../../utils/StringHelperFunctions"; + +interface IParams { + name?: string; + morale?: number; + happiness?: number; + energy?: number; + intelligence?: number; + charisma?: number; + experience?: number; + creativity?: number; + efficiency?: number; + salary?: number; + loc?: string; +} + +export class Employee { + name: string; + mor: number; + hap: number; + ene: number; + int: number; + cha: number; + exp: number; + cre: number; + eff: number; + sal: number; + pro = 0; + cyclesUntilRaise = CorporationConstants.CyclesPerEmployeeRaise; + loc: string; + pos: string; + + constructor(params: IParams = {}) { + this.name = params.name ? params.name : "Bobby"; + + //Morale, happiness, and energy are 0-100 + this.mor = params.morale ? params.morale : getRandomInt(50, 100); + this.hap = params.happiness ? params.happiness : getRandomInt(50, 100); + this.ene = params.energy ? params.energy : getRandomInt(50, 100); + + this.int = params.intelligence ? params.intelligence : getRandomInt(10, 50); + this.cha = params.charisma ? params.charisma : getRandomInt(10, 50); + this.exp = params.experience ? params.experience : getRandomInt(10, 50); + this.cre = params.creativity ? params.creativity : getRandomInt(10, 50); + this.eff = params.efficiency ? params.efficiency : getRandomInt(10, 50); + this.sal = params.salary ? params.salary : getRandomInt(0.1, 5); + + this.loc = params.loc ? params.loc : ""; + this.pos = EmployeePositions.Unassigned; + } + + //Returns the amount the employee needs to be paid + process(marketCycles = 1, office: any) { + const gain = 0.003 * marketCycles, + det = gain * Math.random(); + this.exp += gain; + + // Employee salaries slowly go up over time + this.cyclesUntilRaise -= marketCycles; + if (this.cyclesUntilRaise <= 0) { + this.sal += CorporationConstants.EmployeeRaiseAmount; + this.cyclesUntilRaise += CorporationConstants.CyclesPerEmployeeRaise; + } + + //Training + const trainingEff = gain * Math.random(); + if (this.pos === EmployeePositions.Training) { + //To increase creativity and intelligence special upgrades are needed + this.cha += trainingEff; + this.exp += trainingEff; + this.eff += trainingEff; + } + + this.ene -= det; + this.hap -= det; + + if (this.ene < office.minEne) {this.ene = office.minEne;} + if (this.hap < office.minHap) {this.hap = office.minHap;} + const salary = this.sal * marketCycles * CorporationConstants.SecsPerMarketCycle; + return salary; + } + + calculateProductivity(corporation: any, industry: any): number { + const effCre = this.cre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(), + effCha = this.cha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(), + effInt = this.int * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(), + effEff = this.eff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier(); + const prodBase = this.mor * this.hap * this.ene * 1e-6; + let prodMult = 0; + switch(this.pos) { + //Calculate productivity based on position. This is multipled by prodBase + //to get final value + case EmployeePositions.Operations: + prodMult = (0.6 * effInt) + (0.1 * effCha) + (this.exp) + + (0.5 * effCre) + (effEff); + break; + case EmployeePositions.Engineer: + prodMult = (effInt) + (0.1 * effCha) + (1.5 * this.exp) + + (effEff); + break; + case EmployeePositions.Business: + prodMult = (0.4 * effInt) + (effCha) + (0.5 * this.exp); + break; + case EmployeePositions.Management: + prodMult = (2 * effCha) + (this.exp) + (0.2 * effCre) + + (0.7 * effEff); + break; + case EmployeePositions.RandD: + prodMult = (1.5 * effInt) + (0.8 * this.exp) + (effCre) + + (0.5 * effEff); + break; + case EmployeePositions.Unassigned: + case EmployeePositions.Training: + prodMult = 0; + break; + default: + console.error(`Invalid employee position: ${this.pos}`); + break; + } + return prodBase * prodMult; + } + + //Process benefits from having an office party thrown + throwParty(money: number) { + const mult = 1 + (money / 10e6); + this.mor *= mult; + this.mor = Math.min(100, this.mor); + this.hap *= mult; + this.hap = Math.min(100, this.hap); + return mult; + } + + //'panel' is the DOM element on which to create the UI + createUI(panel: any, corporation: any, industry: any) { + const effCre = this.cre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(), + effCha = this.cha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(), + effInt = this.int * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(), + effEff = this.eff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier(); + panel.style.color = "white"; + panel.appendChild(createElement("p", { + id:"cmpy-mgmt-employee-" + this.name + "-panel-text", + innerHTML:"Morale: " + formatNumber(this.mor, 3) + "
" + + "Happiness: " + formatNumber(this.hap, 3) + "
" + + "Energy: " + formatNumber(this.ene, 3) + "
" + + "Intelligence: " + formatNumber(effInt, 3) + "
" + + "Charisma: " + formatNumber(effCha, 3) + "
" + + "Experience: " + formatNumber(this.exp, 3) + "
" + + "Creativity: " + formatNumber(effCre, 3) + "
" + + "Efficiency: " + formatNumber(effEff, 3) + "
" + + "Salary: " + numeralWrapper.format(this.sal, "$0.000a") + "/ s
", + })); + + //Selector for employee position + const selector = createElement("select", {}) as HTMLSelectElement; + for (const key in EmployeePositions) { + if (EmployeePositions.hasOwnProperty(key)) { + selector.add(createElement("option", { + text: EmployeePositions[key], + value: EmployeePositions[key], + }) as HTMLOptionElement); + } + } + + selector.addEventListener("change", () => { + this.pos = selector.options[selector.selectedIndex].value; + }); + + //Set initial value of selector + for (let i = 0; i < selector.length; ++i) { + if (selector.options[i].value === this.pos) { + selector.selectedIndex = i; + break; + } + } + panel.appendChild(selector); + } + + toJSON(): any { + return Generic_toJSON("Employee", this); + } + + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + static fromJSON(value: any): Employee { + return Generic_fromJSON(Employee, value.data); + } +} + +Reviver.constructors.Employee = Employee; \ No newline at end of file diff --git a/src/Corporation/IDivision.ts b/src/Corporation/IDivision.ts index 1ed13a8e8..53b63e1cf 100644 --- a/src/Corporation/IDivision.ts +++ b/src/Corporation/IDivision.ts @@ -3,5 +3,5 @@ import { IMap } from "../types"; export interface IDivision { name: string; - offices: IMap; + offices: IMap; } diff --git a/src/Corporation/OfficeSpace.ts b/src/Corporation/OfficeSpace.ts new file mode 100644 index 000000000..095c5081e --- /dev/null +++ b/src/Corporation/OfficeSpace.ts @@ -0,0 +1,323 @@ +import { EmployeePositions } from "./EmployeePositions"; +import { CorporationConstants } from "./data/Constants"; +import { getRandomInt } from "../../utils/helpers/getRandomInt"; +import { formatNumber, generateRandomString } from "../../utils/StringHelperFunctions"; +import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver"; +import { yesNoBoxCreate, + yesNoTxtInpBoxCreate, + yesNoBoxGetYesButton, + yesNoBoxGetNoButton, + yesNoTxtInpBoxGetYesButton, + yesNoTxtInpBoxGetNoButton, + yesNoTxtInpBoxGetInput, + yesNoBoxClose, + yesNoTxtInpBoxClose } from "../../utils/YesNoBox"; +import { dialogBoxCreate } from "../../utils/DialogBox"; +import { createPopup } from "../../utils/uiHelpers/createPopup"; +import { removeElementById } from "../../utils/uiHelpers/removeElementById"; +import { createElement } from "../../utils/uiHelpers/createElement"; +import { numeralWrapper } from "../ui/numeralFormat"; +import { Employee } from "./Employee"; + +interface IParams { + loc?: string; + cost?: number; + size?: number; + comfort?: number; + beauty?: number; +} + +export class OfficeSpace { + loc: string; + cost: number; + size: number; + comf: number; + beau: number; + tier = "Basic"; + minEne = 0; + maxEne = 100; + minHap = 0; + maxHap = 100; + maxMor = 100; + employees: any[] = []; + employeeProd: {[key: string]: number} = { + [EmployeePositions.Operations]: 0, + [EmployeePositions.Engineer]: 0, + [EmployeePositions.Business]: 0, + [EmployeePositions.Management]: 0, + [EmployeePositions.RandD]: 0, + total: 0, + }; + + constructor(params: IParams = {}) { + this.loc = params.loc ? params.loc : ""; + this.cost = params.cost ? params.cost : 1; + this.size = params.size ? params.size : 1; + this.comf = params.comfort ? params.comfort : 1; + this.beau = params.beauty ? params.beauty : 1; + } + + + atCapacity(): boolean { + return (this.employees.length) >= this.size; + } + + process(marketCycles = 1, parentRefs: any): number { + const industry = parentRefs.industry; + + // HRBuddy AutoRecruitment and training + if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) { + const emp = this.hireRandomEmployee(); + if (industry.hasResearch("HRBuddy-Training") && emp !== undefined) { + emp.pos = EmployeePositions.Training; + } + } + + // Process Office properties + this.maxEne = 100; + this.maxHap = 100; + this.maxMor = 100; + if (industry.hasResearch("Go-Juice")) { + this.maxEne += 10; + } + if (industry.hasResearch("JoyWire")) { + this.maxHap += 10; + } + if (industry.hasResearch("Sti.mu")) { + this.maxMor += 10; + } + + // Calculate changes in Morale/Happiness/Energy for Employees + let perfMult=1; //Multiplier for employee morale/happiness/energy based on company performance + if (industry.funds < 0 && industry.lastCycleRevenue < 0) { + perfMult = Math.pow(0.99, marketCycles); + } else if (industry.funds > 0 && industry.lastCycleRevenue > 0) { + perfMult = Math.pow(1.01, marketCycles); + } + + const hasAutobrew = industry.hasResearch("AutoBrew"); + const hasAutoparty = industry.hasResearch("AutoPartyManager"); + + let salaryPaid = 0; + for (let i = 0; i < this.employees.length; ++i) { + const emp = this.employees[i]; + if (hasAutoparty) { + emp.mor = this.maxMor; + emp.hap = this.maxHap; + } else { + emp.mor *= perfMult; + emp.hap *= perfMult; + emp.mor = Math.min(emp.mor, this.maxMor); + emp.hap = Math.min(emp.hap, this.maxHap); + } + + if (hasAutobrew) { + emp.ene = this.maxEne; + } else { + emp.ene *= perfMult; + emp.ene = Math.min(emp.ene, this.maxEne); + } + + const salary = emp.process(marketCycles, this); + salaryPaid += salary; + } + + this.calculateEmployeeProductivity(parentRefs); + return salaryPaid; + } + + calculateEmployeeProductivity(parentRefs: any) { + const company = parentRefs.corporation, industry = parentRefs.industry; + + //Reset + for (const name in this.employeeProd) { + this.employeeProd[name] = 0; + } + + let total = 0; + for (let i = 0; i < this.employees.length; ++i) { + const employee = this.employees[i]; + const prod = employee.calculateProductivity(company, industry); + this.employeeProd[employee.pos] += prod; + total += prod; + } + this.employeeProd.total = total; + } + + //Takes care of UI as well + findEmployees(parentRefs: any) { + if (this.atCapacity()) { return; } + if (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) {return;} + + //Generate three random employees (meh, decent, amazing) + const mult1 = getRandomInt(25, 50)/100, + mult2 = getRandomInt(51, 75)/100, + mult3 = getRandomInt(76, 100)/100; + const int = getRandomInt(50, 100), + cha = getRandomInt(50, 100), + exp = getRandomInt(50, 100), + cre = getRandomInt(50, 100), + eff = getRandomInt(50, 100), + sal = CorporationConstants.EmployeeSalaryMultiplier * (int + cha + exp + cre + eff); + + const emp1 = new Employee({ + intelligence: int * mult1, + charisma: cha * mult1, + experience: exp * mult1, + creativity: cre * mult1, + efficiency: eff * mult1, + salary: sal * mult1, + }); + + const emp2 = new Employee({ + intelligence: int * mult2, + charisma: cha * mult2, + experience: exp * mult2, + creativity: cre * mult2, + efficiency: eff * mult2, + salary: sal * mult2, + }); + + const emp3 = new Employee({ + intelligence: int * mult3, + charisma: cha * mult3, + experience: exp * mult3, + creativity: cre * mult3, + efficiency: eff * mult3, + salary: sal * mult3, + }); + + const text = createElement("h1", { + innerHTML: "Select one of the following candidates for hire:", + }); + + const createEmpDiv = function(employee: any, office: any) { + const div = createElement("div", { + class:"cmpy-mgmt-find-employee-option", + innerHTML: "Intelligence: " + formatNumber(employee.int, 1) + "
" + + "Charisma: " + formatNumber(employee.cha, 1) + "
" + + "Experience: " + formatNumber(employee.exp, 1) + "
" + + "Creativity: " + formatNumber(employee.cre, 1) + "
" + + "Efficiency: " + formatNumber(employee.eff, 1) + "
" + + "Salary: " + numeralWrapper.format(employee.sal, '$0.000a') + " \ s
", + clickListener: () => { + office.hireEmployee(employee, parentRefs); + removeElementById("cmpy-mgmt-hire-employee-popup"); + return false; + }, + }); + return div; + }; + + const cancelBtn = createElement("a", { + class:"a-link-button", + innerText:"Cancel", + float:"right", + clickListener:() => { + removeElementById("cmpy-mgmt-hire-employee-popup"); + return false; + }, + }); + + const elems = [text, + createEmpDiv(emp1, this), + createEmpDiv(emp2, this), + createEmpDiv(emp3, this), + cancelBtn]; + + createPopup("cmpy-mgmt-hire-employee-popup", elems); + } + + hireEmployee(employee: Employee, parentRefs: any) { + const company = parentRefs.corporation; + const yesBtn = yesNoTxtInpBoxGetYesButton(), + noBtn = yesNoTxtInpBoxGetNoButton(); + yesBtn.innerHTML = "Hire"; + noBtn.innerHTML = "Cancel"; + yesBtn.addEventListener("click", () => { + const name = yesNoTxtInpBoxGetInput(); + for (let i = 0; i < this.employees.length; ++i) { + if (this.employees[i].name === name) { + dialogBoxCreate("You already have an employee with this nickname! Please give every employee a unique nickname."); + return false; + } + } + employee.name = name; + this.employees.push(employee); + company.rerender(); + return yesNoTxtInpBoxClose(); + }); + noBtn.addEventListener("click", () => { + return yesNoTxtInpBoxClose(); + }); + yesNoTxtInpBoxCreate("Give your employee a nickname!"); + } + + hireRandomEmployee(): Employee | undefined { + if (this.atCapacity()) return; + if (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) return; + + //Generate three random employees (meh, decent, amazing) + const mult = getRandomInt(76, 100)/100; + const int = getRandomInt(50, 100), + cha = getRandomInt(50, 100), + exp = getRandomInt(50, 100), + cre = getRandomInt(50, 100), + eff = getRandomInt(50, 100), + sal = CorporationConstants.EmployeeSalaryMultiplier * (int + cha + exp + cre + eff); + + const emp = new Employee({ + intelligence: int * mult, + charisma: cha * mult, + experience: exp * mult, + creativity: cre * mult, + efficiency: eff * mult, + salary: sal * mult, + }); + + const name = generateRandomString(7); + + for (let i = 0; i < this.employees.length; ++i) { + if (this.employees[i].name === name) { + return this.hireRandomEmployee(); + } + } + emp.name = name; + this.employees.push(emp); + + return emp; + } + + //Finds the first unassigned employee and assigns its to the specified job + assignEmployeeToJob(job: any): 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; + } + + //Finds the first employee with the given job and unassigns it + unassignEmployeeFromJob(job: any): 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; + } + + toJSON(): any { + return Generic_toJSON("OfficeSpace", this); + } + + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + static fromJSON(value: any): OfficeSpace { + return Generic_fromJSON(OfficeSpace, value.data); + } +} + +Reviver.constructors.OfficeSpace = OfficeSpace; \ No newline at end of file diff --git a/src/Corporation/data/Constants.ts b/src/Corporation/data/Constants.ts new file mode 100644 index 000000000..3dce0b1a9 --- /dev/null +++ b/src/Corporation/data/Constants.ts @@ -0,0 +1,60 @@ +const CyclesPerMarketCycle = 50; +const AllCorporationStates = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"]; +export const CorporationConstants: { + INITIALSHARES: number; + SHARESPERPRICEUPDATE: number; + IssueNewSharesCooldown: number; + SellSharesCooldown: number; + CyclesPerMarketCycle: number; + CyclesPerIndustryStateCycle: number; + SecsPerMarketCycle: number; + Cities: string[]; + WarehouseInitialCost: number; + WarehouseInitialSize: number; + WarehouseUpgradeBaseCost: number; + OfficeInitialCost: number; + OfficeInitialSize: number; + OfficeUpgradeBaseCost: number; + BribeThreshold: number; + BribeToRepRatio: number; + ProductProductionCostRatio: number; + DividendMaxPercentage: number; + EmployeeSalaryMultiplier: number; + CyclesPerEmployeeRaise: number; + EmployeeRaiseAmount: number; + BaseMaxProducts: number; + AllCorporationStates: string[]; +} = { + INITIALSHARES: 1e9, //Total number of shares you have at your company + SHARESPERPRICEUPDATE: 1e6, //When selling large number of shares, price is dynamically updated for every batch of this amount + IssueNewSharesCooldown: 216e3, // 12 Hour in terms of game cycles + SellSharesCooldown: 18e3, // 1 Hour in terms of game cycles + + CyclesPerMarketCycle: CyclesPerMarketCycle, + CyclesPerIndustryStateCycle: CyclesPerMarketCycle / AllCorporationStates.length, + SecsPerMarketCycle: CyclesPerMarketCycle / 5, + + Cities: ["Aevum", "Chongqing", "Sector-12", "New Tokyo", "Ishima", "Volhaven"], + + WarehouseInitialCost: 5e9, //Initial purchase cost of warehouse + WarehouseInitialSize: 100, + WarehouseUpgradeBaseCost: 1e9, + + OfficeInitialCost: 4e9, + OfficeInitialSize: 3, + OfficeUpgradeBaseCost: 1e9, + + BribeThreshold: 100e12, //Money needed to be able to bribe for faction rep + BribeToRepRatio: 1e9, //Bribe Value divided by this = rep gain + + ProductProductionCostRatio: 5, //Ratio of material cost of a product to its production cost + + DividendMaxPercentage: 50, + + EmployeeSalaryMultiplier: 3, // Employee stats multiplied by this to determine initial salary + CyclesPerEmployeeRaise: 400, // All employees get a raise every X market cycles + EmployeeRaiseAmount: 50, // Employee salary increases by this (additive) + + BaseMaxProducts: 3, // Initial value for maximum number of products allowed + AllCorporationStates: AllCorporationStates, +}; \ No newline at end of file diff --git a/src/Corporation/ui/CityTabs.tsx b/src/Corporation/ui/CityTabs.tsx index e0f222a49..c774adc1b 100644 --- a/src/Corporation/ui/CityTabs.tsx +++ b/src/Corporation/ui/CityTabs.tsx @@ -2,35 +2,42 @@ // These allow player to navigate between different cities for each industry import React from "react"; import { CityTab } from "./CityTab"; +import { ExpandNewCityPopup } from "./ExpandNewCityPopup"; +import { createPopup } from "../../ui/React/createPopup"; +import { IDivision } from "../IDivision"; interface IProps { eventHandler: any; routing: any; onClicks: {[key: string]: () => void}; city: string; // currentCity - cityStateSetter: any; + cityStateSetter: (city: string) => void; + corp: any; } export function CityTabs(props: IProps): React.ReactElement { const division = props.routing.currentDivision; - const tabs = []; - - // Tabs for each city - for (const cityName in props.onClicks) { - tabs.push( - - ); + function openExpandNewCityModal() { + const popupId = "cmpy-mgmt-expand-city-popup"; + createPopup(popupId, ExpandNewCityPopup, { + popupId: popupId, + corp: props.corp, + division: division as IDivision, + cityStateSetter: props.cityStateSetter, + }); } - tabs.push( + return <> + { + Object.keys(props.onClicks).map((cityName: string) => , + ) + } props.eventHandler.createNewCityPopup(division, props.cityStateSetter)} + current={false} + key={"Expand into new City"} + name={"Expand into new City"} + onClick={openExpandNewCityModal} /> - ); - - return <>{tabs}; + ; } \ No newline at end of file diff --git a/src/Corporation/ui/CorporationUIEventHandler.js b/src/Corporation/ui/CorporationUIEventHandler.js index 687457f55..8b158971c 100644 --- a/src/Corporation/ui/CorporationUIEventHandler.js +++ b/src/Corporation/ui/CorporationUIEventHandler.js @@ -2,7 +2,6 @@ import { CorporationRouting } from "./Routing"; import { Corporation, Industry, - OfficeSpace, Warehouse, DividendMaxPercentage, IssueNewSharesCooldown, @@ -12,6 +11,7 @@ import { Corporation, WarehouseInitialCost, WarehouseInitialSize, BribeToRepRatio } from "../Corporation"; +import { OfficeSpace } from "../OfficeSpace"; import { Industries, IndustryStartingCosts, diff --git a/src/Corporation/ui/ExpandNewCityPopup.tsx b/src/Corporation/ui/ExpandNewCityPopup.tsx new file mode 100644 index 000000000..c9d91087f --- /dev/null +++ b/src/Corporation/ui/ExpandNewCityPopup.tsx @@ -0,0 +1,51 @@ +import React, { useRef } from "react"; +import { IDivision } from "../IDivision"; +import { numeralWrapper } from "../../ui/numeralFormat"; +import { CorporationConstants } from "../data/Constants"; +import { removePopup } from "../../ui/React/createPopup"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; +import { OfficeSpace } from "../OfficeSpace"; + +interface IProps { + popupId: string; + corp: any; + division: IDivision; + cityStateSetter: (city: string) => void; +} + +export function ExpandNewCityPopup(props: IProps): React.ReactElement { + const dropdown = useRef(null); + + function expand(): void { + if(dropdown.current === null) return; + const city = dropdown.current.value; + if (props.corp.funds.lt(CorporationConstants.OfficeInitialCost)) { + dialogBoxCreate("You don't have enough company funds to open a new office!"); + } else { + props.corp.funds = props.corp.funds.minus(CorporationConstants.OfficeInitialCost); + dialogBoxCreate(`Opened a new office in ${city}!`); + props.division.offices[city] = new OfficeSpace({ + loc: city, + size: CorporationConstants.OfficeInitialSize, + }); + } + + props.cityStateSetter(city); + removePopup(props.popupId); + } + return (<> +

+ Would you like to expand into a new city by opening an office? + This would cost {numeralWrapper.format(CorporationConstants.OfficeInitialCost, '$0.000a')} +

+ + + ); +} \ No newline at end of file diff --git a/src/Corporation/ui/HeaderTabs.tsx b/src/Corporation/ui/HeaderTabs.tsx index 1888ba7c6..8ee5b6ff8 100644 --- a/src/Corporation/ui/HeaderTabs.tsx +++ b/src/Corporation/ui/HeaderTabs.tsx @@ -26,8 +26,7 @@ export function HeaderTabs(props: IProps): React.ReactElement { text={props.corp.name} /> { - props.corp.divisions.map((division: IDivision) => - { diff --git a/src/Corporation/ui/Industry.jsx b/src/Corporation/ui/Industry.jsx index c6787e3ed..6d290d467 100644 --- a/src/Corporation/ui/Industry.jsx +++ b/src/Corporation/ui/Industry.jsx @@ -25,7 +25,12 @@ export class Industry extends BaseReactComponent {
- +
) diff --git a/src/Corporation/ui/IndustryOffice.jsx b/src/Corporation/ui/IndustryOffice.jsx index d43cadf22..8f1dfa540 100644 --- a/src/Corporation/ui/IndustryOffice.jsx +++ b/src/Corporation/ui/IndustryOffice.jsx @@ -3,7 +3,7 @@ import React from "react"; import { BaseReactComponent } from "./BaseReactComponent"; -import { OfficeSpace } from "../Corporation"; +import { OfficeSpace } from "../OfficeSpace"; import { EmployeePositions } from "../EmployeePositions"; import { numeralWrapper } from "../../ui/numeralFormat"; diff --git a/src/Corporation/ui/IndustryOverview.jsx b/src/Corporation/ui/IndustryOverview.jsx index 09cdc6a0a..a3e1e2b5b 100644 --- a/src/Corporation/ui/IndustryOverview.jsx +++ b/src/Corporation/ui/IndustryOverview.jsx @@ -3,7 +3,7 @@ import React from "react"; import { BaseReactComponent } from "./BaseReactComponent"; -import { OfficeSpace } from "../Corporation"; +import { OfficeSpace } from "../OfficeSpace"; import { Industries } from "../IndustryData"; import { IndustryUpgrades } from "../IndustryUpgrades"; import { numeralWrapper } from "../../ui/numeralFormat"; diff --git a/src/Corporation/ui/IndustryWarehouse.jsx b/src/Corporation/ui/IndustryWarehouse.tsx similarity index 89% rename from src/Corporation/ui/IndustryWarehouse.jsx rename to src/Corporation/ui/IndustryWarehouse.tsx index d0f220a56..acb217148 100644 --- a/src/Corporation/ui/IndustryWarehouse.jsx +++ b/src/Corporation/ui/IndustryWarehouse.tsx @@ -1,12 +1,9 @@ // React Component for displaying an Industry's warehouse information // (right-side panel in the Industry UI) import React from "react"; -import { BaseReactComponent } from "./BaseReactComponent"; -import { OfficeSpace, - WarehouseInitialCost, - WarehouseUpgradeBaseCost, - ProductProductionCostRatio } from "../Corporation"; +import { CorporationConstants } from "../data/Constants"; +import { OfficeSpace } from "../OfficeSpace"; import { Material } from "../Material"; import { Product } from "../Product"; import { Warehouse } from "../Warehouse"; @@ -15,8 +12,16 @@ import { numeralWrapper } from "../../ui/numeralFormat"; import { isString } from "../../../utils/helpers/isString"; +interface IProductProps { + corp: any; + division: any; + city: string; + product: any; + eventHandler: any; +} + // Creates the UI for a single Product type -function ProductComponent(props) { +function ProductComponent(props: IProductProps) { const corp = props.corp; const division = props.division; const city = props.city; @@ -146,7 +151,7 @@ function ProductComponent(props) {


- Est. Production Cost: {numeralWrapper.formatMoney(product.pCost / ProductProductionCostRatio)} + Est. Production Cost: {numeralWrapper.formatMoney(product.pCost / CorporationConstants.ProductProductionCostRatio)} An estimate of the material cost it takes to create this Product. @@ -181,8 +186,17 @@ function ProductComponent(props) { ) } +interface IMaterialProps { + corp: any; + division: any; + warehouse: any; + city: string; + mat: any; + eventHandler: any; +} + // Creates the UI for a single Material type -function MaterialComponent(props) { +function MaterialComponent(props: any) { const corp = props.corp; const division = props.division; const warehouse = props.warehouse; @@ -230,7 +244,7 @@ function MaterialComponent(props) { sellButtonText += " @ " + numeralWrapper.formatMoney(mat.bCost + markupLimit); } else if (mat.sCost) { if (isString(mat.sCost)) { - var sCost = mat.sCost.replace(/MP/g, mat.bCost); + const sCost = mat.sCost.replace(/MP/g, mat.bCost); sellButtonText += " @ " + numeralWrapper.formatMoney(eval(sCost)); } else { sellButtonText += " @ " + numeralWrapper.formatMoney(mat.sCost); @@ -320,10 +334,17 @@ function MaterialComponent(props) { ) } -export class IndustryWarehouse extends BaseReactComponent { +interface IProps { + corp: any; + routing: any; + currentCity: string; + eventHandler: any; +} + +export function IndustryWarehouse(props: IProps) { // Returns a boolean indicating whether the given material is relevant for the // current industry. - isRelevantMaterial(matName, division) { + function isRelevantMaterial(matName: string, division: any): boolean { // Materials that affect Production multiplier const prodMultiplierMats = ["Hardware", "Robots", "AICores", "RealEstate"]; @@ -334,10 +355,10 @@ export class IndustryWarehouse extends BaseReactComponent { return false; } - renderWarehouseUI() { - const corp = this.corp(); - const division = this.routing().currentDivision; // Validated in render() - const warehouse = division.warehouses[this.props.currentCity]; // Validated in render() + function renderWarehouseUI() { + const corp = props.corp; + const division = props.routing.currentDivision; // Validated in render() + const warehouse = division.warehouses[props.currentCity]; // Validated in render() // General Storage information at the top const sizeUsageStyle = { @@ -346,7 +367,7 @@ export class IndustryWarehouse extends BaseReactComponent { } // Upgrade Warehouse size button - const sizeUpgradeCost = WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1); + const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1); const canAffordUpgrade = (corp.funds.gt(sizeUpgradeCost)); const upgradeWarehouseClass = canAffordUpgrade ? "std-button" : "a-link-button-inactive"; const upgradeWarehouseOnClick = () => { @@ -416,7 +437,7 @@ export class IndustryWarehouse extends BaseReactComponent { // Smart Supply Checkbox const smartSupplyCheckboxId = "cmpy-mgmt-smart-supply-checkbox"; - const smartSupplyOnChange = (e) => { + const smartSupplyOnChange = (e: React.ChangeEvent) => { warehouse.smartSupplyEnabled = e.target.checked; corp.rerender(); } @@ -426,12 +447,12 @@ export class IndustryWarehouse extends BaseReactComponent { for (const matName in warehouse.materials) { if (warehouse.materials[matName] instanceof Material) { // Only create UI for materials that are relevant for the industry - if (this.isRelevantMaterial(matName, division)) { + if (isRelevantMaterial(matName, division)) { mats.push(); @@ -445,13 +466,13 @@ export class IndustryWarehouse extends BaseReactComponent { for (const productName in division.products) { if (division.products[productName] instanceof Product) { products.push(); + />); } } } @@ -500,25 +521,21 @@ export class IndustryWarehouse extends BaseReactComponent { ) } - render() { - const division = this.routing().currentDivision; - if (division == null) { - throw new Error(`Routing does not hold reference to the current Industry`); - } - const warehouse = division.warehouses[this.props.currentCity]; + const division = props.routing.currentDivision; + if (division == null) { + throw new Error(`Routing does not hold reference to the current Industry`); + } + const warehouse = division.warehouses[props.currentCity]; - const newWarehouseOnClick = this.eventHandler().purchaseWarehouse.bind(this.eventHandler(), division, this.props.currentCity); - - if (warehouse instanceof Warehouse) { - return this.renderWarehouseUI(); - } else { - return ( -

- -
- ) - } + if (warehouse instanceof Warehouse) { + return renderWarehouseUI(); + } else { + return ( +
+ +
+ ) } } diff --git a/src/Corporation/ui/LevelableUpgrade.jsx b/src/Corporation/ui/LevelableUpgrade.jsx deleted file mode 100644 index 937652d8f..000000000 --- a/src/Corporation/ui/LevelableUpgrade.jsx +++ /dev/null @@ -1,36 +0,0 @@ -// React components for the levelable upgrade buttons on the overview panel -import React from "react"; -import { BaseReactComponent } from "./BaseReactComponent"; - -import { numeralWrapper } from "../../ui/numeralFormat"; -import { dialogBoxCreate } from "../../../utils/DialogBox"; - -export class LevelableUpgrade extends BaseReactComponent { - render() { - const data = this.props.upgradeData; - const level = this.props.upgradeLevel; - - const baseCost = data[1]; - const priceMult = data[2]; - const cost = baseCost * Math.pow(priceMult, level); - - const text = `${data[4]} - ${numeralWrapper.formatMoney(cost)}` - const tooltip = data[5]; - const onClick = () => { - const corp = this.corp(); - if (corp.funds.lt(cost)) { - dialogBoxCreate("Insufficient funds"); - } else { - corp.upgrade(data); - corp.rerender(); - } - } - - return ( -
- {text} - {tooltip} -
- ) - } -} diff --git a/src/Corporation/ui/LevelableUpgrade.tsx b/src/Corporation/ui/LevelableUpgrade.tsx new file mode 100644 index 000000000..8b8c69d8a --- /dev/null +++ b/src/Corporation/ui/LevelableUpgrade.tsx @@ -0,0 +1,39 @@ +// React components for the levelable upgrade buttons on the overview panel +import React from "react"; + +import { numeralWrapper } from "../../ui/numeralFormat"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; + +interface IProps { + upgradeData: number[]; + upgradeLevel: number; + corp: any; +} + +export function LevelableUpgrade(props: IProps): React.ReactElement { + const data = props.upgradeData; + const level = props.upgradeLevel; + + const baseCost = data[1]; + const priceMult = data[2]; + const cost = baseCost * Math.pow(priceMult, level); + + const text = `${data[4]} - ${numeralWrapper.formatMoney(cost)}` + const tooltip = data[5]; + function onClick() { + const corp = props.corp; + if (corp.funds.lt(cost)) { + dialogBoxCreate("Insufficient funds"); + } else { + corp.upgrade(data); + corp.rerender(); + } + } + + return ( +
+ {text} + {tooltip} +
+ ) +} diff --git a/src/Corporation/ui/MainPanel.jsx b/src/Corporation/ui/MainPanel.jsx index 3a632639c..5ed4cad31 100644 --- a/src/Corporation/ui/MainPanel.jsx +++ b/src/Corporation/ui/MainPanel.jsx @@ -8,7 +8,7 @@ import { CityTabs } from "./CityTabs"; import { Industry } from "./Industry"; import { Overview } from "./Overview"; -import { OfficeSpace } from "../Corporation"; +import { OfficeSpace } from "../OfficeSpace"; import { CityName } from "../../Locations/data/CityNames"; @@ -80,6 +80,7 @@ export class MainPanel extends BaseReactComponent { const cityTabs = ( + {props.text} { hasTooltip && @@ -33,59 +37,58 @@ export class Overview extends BaseReactComponent { } ) - } // Returns a string with general information about Corporation - getOverviewText() { + function getOverviewText() { // Formatted text for profit - var profit = this.corp().revenue.minus(this.corp().expenses).toNumber(), + const profit = props.corp.revenue.minus(props.corp.expenses).toNumber(), profitStr = profit >= 0 ? numeralWrapper.formatMoney(profit) : "-" + numeralWrapper.format(-1 * profit, "$0.000a"); // Formatted text for dividend information, if applicable let dividendStr = ""; - if (this.corp().dividendPercentage > 0 && profit > 0) { - const totalDividends = (this.corp().dividendPercentage / 100) * profit; + if (props.corp.dividendPercentage > 0 && profit > 0) { + const totalDividends = (props.corp.dividendPercentage / 100) * profit; const retainedEarnings = profit - totalDividends; - const dividendsPerShare = totalDividends / this.corp().totalShares; - const playerEarnings = this.corp().numShares * dividendsPerShare; + const dividendsPerShare = totalDividends / props.corp.totalShares; + const playerEarnings = props.corp.numShares * dividendsPerShare; dividendStr = `Retained Profits (after dividends): ${numeralWrapper.format(retainedEarnings, "$0.000a")} / s

` + - `Dividend Percentage: ${numeralWrapper.format(this.corp().dividendPercentage / 100, "0%")}
` + + `Dividend Percentage: ${numeralWrapper.format(props.corp.dividendPercentage / 100, "0%")}
` + `Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s
` + `Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s
` + - `Dividend Tax Rate: ${this.corp().dividendTaxPercentage}%
` + - `Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (this.corp().dividendTaxPercentage / 100)), "$0.000a")} / s

`; + `Dividend Tax Rate: ${props.corp.dividendTaxPercentage}%
` + + `Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (props.corp.dividendTaxPercentage / 100)), "$0.000a")} / s

`; } - let txt = "Total Funds: " + numeralWrapper.format(this.corp().funds.toNumber(), '$0.000a') + "
" + - "Total Revenue: " + numeralWrapper.format(this.corp().revenue.toNumber(), "$0.000a") + " / s
" + - "Total Expenses: " + numeralWrapper.format(this.corp().expenses.toNumber(), "$0.000a") + " / s
" + + let txt = "Total Funds: " + numeralWrapper.format(props.corp.funds.toNumber(), '$0.000a') + "
" + + "Total Revenue: " + numeralWrapper.format(props.corp.revenue.toNumber(), "$0.000a") + " / s
" + + "Total Expenses: " + numeralWrapper.format(props.corp.expenses.toNumber(), "$0.000a") + " / s
" + "Total Profits: " + profitStr + " / s
" + dividendStr + - "Publicly Traded: " + (this.corp().public ? "Yes" : "No") + "
" + - "Owned Stock Shares: " + numeralWrapper.format(this.corp().numShares, '0.000a') + "
" + - "Stock Price: " + (this.corp().public ? numeralWrapper.formatMoney(this.corp().sharePrice) : "N/A") + "
" + - "

Total Stock Shares: " + numeralWrapper.format(this.corp().totalShares, "0.000a") + + "Publicly Traded: " + (props.corp.public ? "Yes" : "No") + "
" + + "Owned Stock Shares: " + numeralWrapper.format(props.corp.numShares, '0.000a') + "
" + + "Stock Price: " + (props.corp.public ? numeralWrapper.formatMoney(props.corp.sharePrice) : "N/A") + "
" + + "

Total Stock Shares: " + numeralWrapper.format(props.corp.totalShares, "0.000a") + "" + - `Outstanding Shares: ${numeralWrapper.format(this.corp().issuedShares, "0.000a")}
` + - `Private Shares: ${numeralWrapper.format(this.corp().totalShares - this.corp().issuedShares - this.corp().numShares, "0.000a")}` + + `Outstanding Shares: ${numeralWrapper.format(props.corp.issuedShares, "0.000a")}
` + + `Private Shares: ${numeralWrapper.format(props.corp.totalShares - props.corp.issuedShares - props.corp.numShares, "0.000a")}` + "



"; - const storedTime = this.corp().storedCycles * CONSTANTS.MilliPerCycle; + const storedTime = props.corp.storedCycles * CONSTANTS.MilliPerCycle; if (storedTime > 15000) { txt += `Bonus time: ${convertTimeMsToTimeElapsedString(storedTime)}

`; } - let prodMult = this.corp().getProductionMultiplier(), - storageMult = this.corp().getStorageMultiplier(), - advMult = this.corp().getAdvertisingMultiplier(), - empCreMult = this.corp().getEmployeeCreMultiplier(), - empChaMult = this.corp().getEmployeeChaMultiplier(), - empIntMult = this.corp().getEmployeeIntMultiplier(), - empEffMult = this.corp().getEmployeeEffMultiplier(), - salesMult = this.corp().getSalesMultiplier(), - sciResMult = this.corp().getScientificResearchMultiplier(); + const prodMult = props.corp.getProductionMultiplier(), + storageMult = props.corp.getStorageMultiplier(), + advMult = props.corp.getAdvertisingMultiplier(), + empCreMult = props.corp.getEmployeeCreMultiplier(), + empChaMult = props.corp.getEmployeeChaMultiplier(), + empIntMult = props.corp.getEmployeeIntMultiplier(), + empEffMult = props.corp.getEmployeeEffMultiplier(), + salesMult = props.corp.getSalesMultiplier(), + sciResMult = props.corp.getScientificResearchMultiplier(); if (prodMult > 1) {txt += "Production Multiplier: " + numeralWrapper.format(prodMult, "0.000") + "
";} if (storageMult > 1) {txt += "Storage Multiplier: " + numeralWrapper.format(storageMult, "0.000") + "
";} if (advMult > 1) {txt += "Advertising Multiplier: " + numeralWrapper.format(advMult, "0.000") + "
";} @@ -101,11 +104,11 @@ export class Overview extends BaseReactComponent { // Render the buttons that lie below the overview text. // These are mainly for things such as managing finances/stock - renderButtons() { + function renderButtons() { // Create a "Getting Started Guide" button that lets player view the // handbook and adds it to the players home computer - const getStarterGuideOnClick = this.corp().getStarterGuide.bind(this.corp()); - const getStarterGuideBtn = this.createButton({ + const getStarterGuideOnClick = props.corp.getStarterGuide.bind(props.corp); + const getStarterGuideBtn = createButton({ class: "a-link-button", display: "inline-block", onClick: getStarterGuideOnClick, @@ -117,13 +120,12 @@ export class Overview extends BaseReactComponent { // Create a "Bribe Factions" button if your Corporation is powerful enough. // This occurs regardless of whether you're public or private - const canBribe = (this.corp().determineValuation() >= BribeThreshold); - const bribeFactionsOnClick = this.eventHandler().createBribeFactionsPopup.bind(this.eventHandler()); + const canBribe = (props.corp.determineValuation() >= CorporationConstants.BribeThreshold); const bribeFactionsClass = (canBribe ? "a-link-button" : "a-link-button-inactive"); - const bribeFactionsBtn = this.createButton({ + const bribeFactionsBtn = createButton({ class: bribeFactionsClass, display: "inline-block", - onClick: bribeFactionsOnClick, + onClick: props.eventHandler.createBribeFactionsPopup, text: "Bribe Factions", tooltip: (canBribe ? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation" @@ -136,34 +138,36 @@ export class Overview extends BaseReactComponent { getStarterGuide: getStarterGuideBtn, }; - if (this.corp().public) { - return this.renderPublicButtons(generalBtns); + if (props.corp.public) { + return renderPublicButtons(generalBtns); } else { - return this.renderPrivateButtons(generalBtns); + return renderPrivateButtons(generalBtns); } } // Render the buttons for when your Corporation is still private - renderPrivateButtons(generalBtns) { - const fundingAvailable = (this.corp().fundingRound < 4); + function renderPrivateButtons(generalBtns: any) { + const fundingAvailable = (props.corp.fundingRound < 4); const findInvestorsClassName = fundingAvailable ? "std-button" : "a-link-button-inactive"; const findInvestorsTooltip = fundingAvailable ? "Search for private investors who will give you startup funding in exchangefor equity (stock shares) in your company" : null; - const findInvestorsOnClick = this.corp().getInvestment.bind(this.corp()); - const goPublicOnClick = this.corp().goPublic.bind(this.corp()); + const findInvestorsOnClick = props.corp.getInvestment.bind(props.corp); + const goPublicOnClick = props.corp.goPublic.bind(props.corp); - const findInvestorsBtn = this.createButton({ + const findInvestorsBtn = createButton({ class: findInvestorsClassName, onClick: findInvestorsOnClick, style: "inline-block", text: "Find Investors", tooltip: findInvestorsTooltip, + display: "inline-block", }); - const goPublicBtn = this.createButton({ + const goPublicBtn = createButton({ class: "std-button", onClick: goPublicOnClick, style: "inline-block", + display: "inline-block", text: "Go Public", tooltip: "Become a publicly traded and owned entity. Going public " + "involves issuing shares for an IPO. Once you are a public " + @@ -183,10 +187,9 @@ export class Overview extends BaseReactComponent { } // Render the buttons for when your Corporation has gone public - renderPublicButtons(generalBtns) { - const corp = this.corp(); + function renderPublicButtons(generalBtns: any) { + const corp = props.corp; - const sellSharesOnClick = this.eventHandler().createSellSharesPopup.bind(this.eventHandler()); const sellSharesOnCd = (corp.shareSaleCooldown > 0); const sellSharesClass = sellSharesOnCd ? "a-link-button-inactive" : "std-button"; const sellSharesTooltip = sellSharesOnCd @@ -194,45 +197,42 @@ export class Overview extends BaseReactComponent { : "Sell your shares in the company. The money earned from selling your " + "shares goes into your personal account, not the Corporation's. " + "This is one of the only ways to profit from your business venture." - const sellSharesBtn = this.createButton({ + const sellSharesBtn = createButton({ class: sellSharesClass, display: "inline-block", - onClick: function(event) { + onClick: function(event: MouseEvent) { if(!event.isTrusted) return; - sellSharesOnClick(event); + props.eventHandler.createSellSharesPopup(event); }, text: "Sell Shares", tooltip: sellSharesTooltip, }); - const buybackSharesOnClick = this.eventHandler().createBuybackSharesPopup.bind(this.eventHandler()); - const buybackSharesBtn = this.createButton({ + const buybackSharesBtn = createButton({ class: "std-button", display: "inline-block", - onClick: buybackSharesOnClick, + onClick: props.eventHandler.createBuybackSharesPopup, text: "Buyback shares", tooltip: "Buy back shares you that previously issued or sold at market price.", }); - const issueNewSharesOnClick = this.eventHandler().createIssueNewSharesPopup.bind(this.eventHandler()); const issueNewSharesOnCd = (corp.issueNewSharesCooldown > 0); const issueNewSharesClass = issueNewSharesOnCd ? "a-link-button-inactive" : "std-button"; const issueNewSharesTooltip = issueNewSharesOnCd ? "Cannot issue new shares for " + corp.convertCooldownToString(corp.issueNewSharesCooldown) : "Issue new equity shares to raise capital."; - const issueNewSharesBtn = this.createButton({ + const issueNewSharesBtn = createButton({ class: issueNewSharesClass, display: "inline-block", - onClick: issueNewSharesOnClick, + onClick: props.eventHandler.createIssueNewSharesPopup, text: "Issue New Shares", tooltip: issueNewSharesTooltip, }); - const issueDividendsOnClick = this.eventHandler().createIssueDividendsPopup.bind(this.eventHandler()); - const issueDividendsBtn = this.createButton({ + const issueDividendsBtn = createButton({ class: "std-button", display: "inline-block", - onClick: issueDividendsOnClick, + onClick: props.eventHandler.createIssueDividendsPopup, text: "Issue Dividends", tooltip: "Manage the dividends that are paid out to shareholders (including yourself)", }); @@ -252,23 +252,27 @@ export class Overview extends BaseReactComponent { } // Render the UI for Corporation upgrades - renderUpgrades() { + function renderUpgrades() { // Don't show upgrades - if (this.corp().divisions.length <= 0) { return; } + if (props.corp.divisions.length <= 0) { return; } // Create an array of all Unlocks - const unlockUpgrades = []; + const unlockUpgrades: React.ReactElement[] = []; Object.values(CorporationUnlockUpgrades).forEach((unlockData) => { - if (this.corp().unlockUpgrades[unlockData[0]] === 0) { - unlockUpgrades.push(this.renderUnlockUpgrade(unlockData)); + if (props.corp.unlockUpgrades[unlockData[0]] === 0) { + unlockUpgrades.push(); } }); // Create an array of properties of all unlocks const levelableUpgradeProps = []; - for (let i = 0; i < this.corp().upgrades.length; ++i) { + for (let i = 0; i < props.corp.upgrades.length; ++i) { const upgradeData = CorporationUpgrades[i]; - const level = this.corp().upgrades[i]; + const level = props.corp.upgrades[i]; levelableUpgradeProps.push({ upgradeData: upgradeData, @@ -284,44 +288,25 @@ export class Overview extends BaseReactComponent {

Upgrades

{ - levelableUpgradeProps.map((data) => { - return this.renderLevelableUpgrade(data); - }) + levelableUpgradeProps.map((data: any) => , + ) } ) } - renderUnlockUpgrade(data) { - return ( - - ) - } - renderLevelableUpgrade(data) { - return ( - - ) - - } - - render() { - return ( -
-

- {this.renderButtons()} -
- {this.renderUpgrades()} -
- ) - } + return ( +
+

+ {renderButtons()} +
+ {renderUpgrades()} +
+ ) } diff --git a/src/Corporation/ui/UnlockUpgrade.jsx b/src/Corporation/ui/UnlockUpgrade.jsx deleted file mode 100644 index e719c73c7..000000000 --- a/src/Corporation/ui/UnlockUpgrade.jsx +++ /dev/null @@ -1,30 +0,0 @@ -// React Components for the Unlock upgrade buttons on the overview page -import React from "react"; -import { BaseReactComponent } from "./BaseReactComponent"; - -import { numeralWrapper } from "../../ui/numeralFormat"; -import { dialogBoxCreate } from "../../../utils/DialogBox"; - -export class UnlockUpgrade extends BaseReactComponent { - render() { - const data = this.props.upgradeData; - const text = `${data[2]} - ${numeralWrapper.formatMoney(data[1])}`; - const tooltip = data[3]; - const onClick = () => { - const corp = this.corp(); - if (corp.funds.lt(data[1])) { - dialogBoxCreate("Insufficient funds"); - } else { - corp.unlock(data); - corp.rerender(); - } - } - - return ( -
- {text} - {tooltip} -
- ) - } -} diff --git a/src/Corporation/ui/UnlockUpgrade.tsx b/src/Corporation/ui/UnlockUpgrade.tsx new file mode 100644 index 000000000..beb7965bf --- /dev/null +++ b/src/Corporation/ui/UnlockUpgrade.tsx @@ -0,0 +1,32 @@ +// React Components for the Unlock upgrade buttons on the overview page +import React from "react"; + +import { numeralWrapper } from "../../ui/numeralFormat"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; + +interface IProps { + upgradeData: number[]; + corp: any; +} + +export function UnlockUpgrade(props: IProps): React.ReactElement { + const data = props.upgradeData; + const text = `${data[2]} - ${numeralWrapper.formatMoney(data[1])}`; + const tooltip = data[3]; + function onClick() { + const corp = props.corp; + if (corp.funds.lt(data[1])) { + dialogBoxCreate("Insufficient funds"); + } else { + corp.unlock(data); + corp.rerender(); + } + } + + return ( +
+ {text} + {tooltip} +
+ ) +} diff --git a/src/InteractiveTutorial.d.ts b/src/InteractiveTutorial.d.ts index fb971c533..d99d083d8 100644 --- a/src/InteractiveTutorial.d.ts +++ b/src/InteractiveTutorial.d.ts @@ -1,3 +1,3 @@ export declare function iTutorialNextStep(): void; -export declare const ITutorial: {isRunning: boolean, currStep: number}; +export declare const ITutorial: {isRunning: boolean; currStep: number}; export declare const iTutorialSteps: {[key: string]: number}; \ No newline at end of file