more convertion

This commit is contained in:
Olivier Gagnon 2021-08-28 02:50:06 -04:00
parent 3d2aeb63a0
commit 4b53d6ecf7
20 changed files with 889 additions and 693 deletions

@ -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) + "<br>" +
"Happiness: " + formatNumber(this.hap, 3) + "<br>" +
"Energy: " + formatNumber(this.ene, 3) + "<br>" +
"Intelligence: " + formatNumber(effInt, 3) + "<br>" +
"Charisma: " + formatNumber(effCha, 3) + "<br>" +
"Experience: " + formatNumber(this.exp, 3) + "<br>" +
"Creativity: " + formatNumber(effCre, 3) + "<br>" +
"Efficiency: " + formatNumber(effEff, 3) + "<br>" +
"Salary: " + numeralWrapper.format(this.sal, "$0.000a") + "/ s<br>",
}));
//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) + "<br>" +
"Charisma: " + formatNumber(employee.cha, 1) + "<br>" +
"Experience: " + formatNumber(employee.exp, 1) + "<br>" +
"Creativity: " + formatNumber(employee.cre, 1) + "<br>" +
"Efficiency: " + formatNumber(employee.eff, 1) + "<br>" +
"Salary: " + numeralWrapper.format(employee.sal, '$0.000a') + " \ s<br>",
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};

194
src/Corporation/Employee.ts Normal file

@ -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) + "<br>" +
"Happiness: " + formatNumber(this.hap, 3) + "<br>" +
"Energy: " + formatNumber(this.ene, 3) + "<br>" +
"Intelligence: " + formatNumber(effInt, 3) + "<br>" +
"Charisma: " + formatNumber(effCha, 3) + "<br>" +
"Experience: " + formatNumber(this.exp, 3) + "<br>" +
"Creativity: " + formatNumber(effCre, 3) + "<br>" +
"Efficiency: " + formatNumber(effEff, 3) + "<br>" +
"Salary: " + numeralWrapper.format(this.sal, "$0.000a") + "/ s<br>",
}));
//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;

@ -3,5 +3,5 @@ import { IMap } from "../types";
export interface IDivision {
name: string;
offices: IMap<IOfficeSpace>;
offices: IMap<IOfficeSpace | number>;
}

@ -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) + "<br>" +
"Charisma: " + formatNumber(employee.cha, 1) + "<br>" +
"Experience: " + formatNumber(employee.exp, 1) + "<br>" +
"Creativity: " + formatNumber(employee.cre, 1) + "<br>" +
"Efficiency: " + formatNumber(employee.eff, 1) + "<br>" +
"Salary: " + numeralWrapper.format(employee.sal, '$0.000a') + " \ s<br>",
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;

@ -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,
};

@ -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(
<CityTab current={props.city === cityName} key={cityName} name={cityName} onClick={props.onClicks[cityName]} />
);
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) => <CityTab current={props.city === cityName} key={cityName} name={cityName} onClick={props.onClicks[cityName]} />,
)
}
<CityTab
current={false}
key={"Expand into new City"}
name={"Expand into new City"}
onClick={() => props.eventHandler.createNewCityPopup(division, props.cityStateSetter)}
current={false}
key={"Expand into new City"}
name={"Expand into new City"}
onClick={openExpandNewCityModal}
/>
);
return <>{tabs}</>;
</>;
}

@ -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,

@ -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<HTMLSelectElement>(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 (<>
<p>
Would you like to expand into a new city by opening an office?
This would cost {numeralWrapper.format(CorporationConstants.OfficeInitialCost, '$0.000a')}
</p>
<select ref={dropdown} className="dropdown" style={{margin:"5px"}}>
{
Object.keys(props.division.offices)
.filter((cityName: string) => props.division.offices[cityName] === 0)
.map((cityName: string) => <option key={cityName} value={cityName}>{cityName}</option>,
)
}
</select>
<button className="std-button" style={{display:"inline-block"}} onClick={expand}>Confirm</button>
</>);
}

@ -26,8 +26,7 @@ export function HeaderTabs(props: IProps): React.ReactElement {
text={props.corp.name}
/>
{
props.corp.divisions.map((division: IDivision) =>
<HeaderTab
props.corp.divisions.map((division: IDivision) => <HeaderTab
current={props.routing.isOn(division.name)}
key={division.name}
onClick={() => {

@ -25,7 +25,12 @@ export class Industry extends BaseReactComponent {
</div>
<div className={"cmpy-mgmt-industry-right-panel"}>
<IndustryWarehouse {...this.props} />
<IndustryWarehouse
corp={this.props.corp}
routing={this.props.routing}
currentCity={this.props.currentCity}
eventHandler={this.props.eventHandler}
/>
</div>
</div>
)

@ -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";

@ -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";

@ -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) {
</span>
</p><br />
<p className={"tooltip"}>
Est. Production Cost: {numeralWrapper.formatMoney(product.pCost / ProductProductionCostRatio)}
Est. Production Cost: {numeralWrapper.formatMoney(product.pCost / CorporationConstants.ProductProductionCostRatio)}
<span className={"tooltiptext"}>
An estimate of the material cost it takes to create this Product.
</span>
@ -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<HTMLInputElement>) => {
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(<MaterialComponent
city={this.props.currentCity}
city={props.currentCity}
corp={corp}
division={division}
eventHandler={this.eventHandler()}
eventHandler={props.eventHandler}
key={matName}
mat={warehouse.materials[matName]}
warehouse={warehouse} />);
@ -445,13 +466,13 @@ export class IndustryWarehouse extends BaseReactComponent {
for (const productName in division.products) {
if (division.products[productName] instanceof Product) {
products.push(<ProductComponent
city={this.props.currentCity}
city={props.currentCity}
corp={corp}
division={division}
eventHandler={this.eventHandler()}
eventHandler={props.eventHandler}
key={productName}
product={division.products[productName]}
warehouse={warehouse} />);
/>);
}
}
}
@ -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 (
<div className={"cmpy-mgmt-warehouse-panel"}>
<button className={"std-button"} onClick={newWarehouseOnClick}>
Purchase Warehouse ({numeralWrapper.formatMoney(WarehouseInitialCost)})
</button>
</div>
)
}
if (warehouse instanceof Warehouse) {
return renderWarehouseUI();
} else {
return (
<div className={"cmpy-mgmt-warehouse-panel"}>
<button className={"std-button"} onClick={() => props.eventHandler.purchaseWarehouse(division, props.currentCity)}>
Purchase Warehouse ({numeralWrapper.formatMoney(CorporationConstants.WarehouseInitialCost)})
</button>
</div>
)
}
}

@ -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 (
<div className={"cmpy-mgmt-upgrade-div tooltip"} style={{"width" : "45%"}} onClick={onClick}>
{text}
<span className={"tooltiptext"}>{tooltip}</span>
</div>
)
}
}

@ -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 (
<div className={"cmpy-mgmt-upgrade-div tooltip"} style={{"width" : "45%"}} onClick={onClick}>
{text}
<span className={"tooltiptext"}>{tooltip}</span>
</div>
)
}

@ -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 = (
<CityTabs
{...this.props}
corp={this.props.corp}
city={this.state.city}
onClicks={onClicks}
cityStateSetter={this.changeCityState.bind(this)}

@ -1,10 +1,9 @@
// React Component for displaying Corporation Overview info
import React from "react";
import { BaseReactComponent } from "./BaseReactComponent";
import { LevelableUpgrade } from "./LevelableUpgrade";
import { UnlockUpgrade } from "./UnlockUpgrade";
import { BribeThreshold } from "../Corporation";
import { CorporationConstants } from "../data/Constants";
import { CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades";
import { CorporationUpgrades } from "../data/CorporationUpgrades";
@ -12,9 +11,14 @@ import { CONSTANTS } from "../../Constants";
import { numeralWrapper } from "../../ui/numeralFormat";
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
export class Overview extends BaseReactComponent {
interface IProps {
corp: any;
eventHandler: any;
}
export function Overview(props: IProps): React.ReactElement {
// Generic Function for Creating a button
createButton(props) {
function createButton(props: any) {
let className = props.class ? props.class : "std-button";
const displayStyle = props.display ? props.display : "block";
const hasTooltip = (props.tooltip != null);
@ -23,7 +27,7 @@ export class Overview extends BaseReactComponent {
}
return (
<a className={className} onClick={props.onClick} style={{display: {displayStyle}}}>
<a className={className} onClick={props.onClick} style={{display: displayStyle}}>
{props.text}
{
hasTooltip &&
@ -33,59 +37,58 @@ export class Overview extends BaseReactComponent {
}
</a>
)
}
// 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<br><br>` +
`Dividend Percentage: ${numeralWrapper.format(this.corp().dividendPercentage / 100, "0%")}<br>` +
`Dividend Percentage: ${numeralWrapper.format(props.corp.dividendPercentage / 100, "0%")}<br>` +
`Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s<br>` +
`Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s<br>` +
`Dividend Tax Rate: ${this.corp().dividendTaxPercentage}%<br>` +
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (this.corp().dividendTaxPercentage / 100)), "$0.000a")} / s<br><br>`;
`Dividend Tax Rate: ${props.corp.dividendTaxPercentage}%<br>` +
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (props.corp.dividendTaxPercentage / 100)), "$0.000a")} / s<br><br>`;
}
let txt = "Total Funds: " + numeralWrapper.format(this.corp().funds.toNumber(), '$0.000a') + "<br>" +
"Total Revenue: " + numeralWrapper.format(this.corp().revenue.toNumber(), "$0.000a") + " / s<br>" +
"Total Expenses: " + numeralWrapper.format(this.corp().expenses.toNumber(), "$0.000a") + " / s<br>" +
let txt = "Total Funds: " + numeralWrapper.format(props.corp.funds.toNumber(), '$0.000a') + "<br>" +
"Total Revenue: " + numeralWrapper.format(props.corp.revenue.toNumber(), "$0.000a") + " / s<br>" +
"Total Expenses: " + numeralWrapper.format(props.corp.expenses.toNumber(), "$0.000a") + " / s<br>" +
"Total Profits: " + profitStr + " / s<br>" +
dividendStr +
"Publicly Traded: " + (this.corp().public ? "Yes" : "No") + "<br>" +
"Owned Stock Shares: " + numeralWrapper.format(this.corp().numShares, '0.000a') + "<br>" +
"Stock Price: " + (this.corp().public ? numeralWrapper.formatMoney(this.corp().sharePrice) : "N/A") + "<br>" +
"<p class='tooltip'>Total Stock Shares: " + numeralWrapper.format(this.corp().totalShares, "0.000a") +
"Publicly Traded: " + (props.corp.public ? "Yes" : "No") + "<br>" +
"Owned Stock Shares: " + numeralWrapper.format(props.corp.numShares, '0.000a') + "<br>" +
"Stock Price: " + (props.corp.public ? numeralWrapper.formatMoney(props.corp.sharePrice) : "N/A") + "<br>" +
"<p class='tooltip'>Total Stock Shares: " + numeralWrapper.format(props.corp.totalShares, "0.000a") +
"<span class='tooltiptext'>" +
`Outstanding Shares: ${numeralWrapper.format(this.corp().issuedShares, "0.000a")}<br>` +
`Private Shares: ${numeralWrapper.format(this.corp().totalShares - this.corp().issuedShares - this.corp().numShares, "0.000a")}` +
`Outstanding Shares: ${numeralWrapper.format(props.corp.issuedShares, "0.000a")}<br>` +
`Private Shares: ${numeralWrapper.format(props.corp.totalShares - props.corp.issuedShares - props.corp.numShares, "0.000a")}` +
"</span></p><br><br>";
const storedTime = this.corp().storedCycles * CONSTANTS.MilliPerCycle;
const storedTime = props.corp.storedCycles * CONSTANTS.MilliPerCycle;
if (storedTime > 15000) {
txt += `Bonus time: ${convertTimeMsToTimeElapsedString(storedTime)}<br><br>`;
}
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") + "<br>";}
if (storageMult > 1) {txt += "Storage Multiplier: " + numeralWrapper.format(storageMult, "0.000") + "<br>";}
if (advMult > 1) {txt += "Advertising Multiplier: " + numeralWrapper.format(advMult, "0.000") + "<br>";}
@ -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(<UnlockUpgrade
corp={props.corp}
upgradeData={unlockData}
key={unlockData[0]}
/>);
}
});
// 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 {
<h1 className={"cmpy-mgmt-upgrade-header"}> Upgrades </h1>
{
levelableUpgradeProps.map((data) => {
return this.renderLevelableUpgrade(data);
})
levelableUpgradeProps.map((data: any) => <LevelableUpgrade
corp={props.corp}
upgradeData={data.upgradeData}
upgradeLevel={data.upgradeLevel}
key={data.upgradeData[0]}
/>,
)
}
</div>
)
}
renderUnlockUpgrade(data) {
return (
<UnlockUpgrade
{...this.props}
upgradeData={data}
key={data[0]}
/>
)
}
renderLevelableUpgrade(data) {
return (
<LevelableUpgrade
{...this.props}
upgradeData={data.upgradeData}
upgradeLevel={data.upgradeLevel}
key={data.upgradeData[0]}
/>
)
}
render() {
return (
<div>
<p dangerouslySetInnerHTML={{__html: this.getOverviewText()}}></p>
{this.renderButtons()}
<br />
{this.renderUpgrades()}
</div>
)
}
return (
<div>
<p dangerouslySetInnerHTML={{__html: getOverviewText()}}></p>
{renderButtons()}
<br />
{renderUpgrades()}
</div>
)
}

@ -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 (
<div className={"cmpy-mgmt-upgrade-div tooltip"} style={{"width" : "45%"}} onClick={onClick}>
{text}
<span className={"tooltiptext"}>{tooltip}</span>
</div>
)
}
}

@ -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 (
<div className={"cmpy-mgmt-upgrade-div tooltip"} style={{"width" : "45%"}} onClick={onClick}>
{text}
<span className={"tooltiptext"}>{tooltip}</span>
</div>
)
}

@ -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};