Fixed numerous bugs with the recent Corporation UI rewrite. Rebalanced Corporation mechanic to give employees better and more interesting effects

This commit is contained in:
danielyxie 2019-03-15 02:37:06 -07:00
parent 8470f307ac
commit c3ecc189fd
19 changed files with 394 additions and 214 deletions

@ -5,7 +5,7 @@ played at https://danielyxie.github.io/bitburner.
# Documentation
The game's official documentation can be found on [Read The
Docs](http://bitburner.readthedocs.io/). Please note that this is still a
work-in-progress and is in its early stages.
work-in-progress.
The documentation is created using [Sphinx](http://www.sphinx-doc.org).
@ -14,11 +14,6 @@ files](/doc/source) and then making a pull request with your contributions.
For further guidance, please refer to the "As A Documentor" section of
[CONTRIBUTING](CONTRIBUTING.md).
# Wiki
The game's wiki can be found on [Wikia](http://bitburner.wikia.com/). Please
note that the wiki is in the process of being deprecated. Eventually all of
the wiki content will be moved into the Read The Docs documentation.
# Contribution
There are many ways to contribute to the game. It can be as simple as fixing
a typo, correcting a bug, or improving the UI. For guidance on doing so,

@ -159,5 +159,6 @@
/* Research */
#corporation-research-popup-box-content {
overflow-x: visible !important;
overflow-x: auto !important;
overflow-y: auto !important;
}

@ -258,6 +258,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
BitNodeMultipliers.FactionPassiveRepGain = 0;
break;
case 3: //Corporatocracy
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
BitNodeMultipliers.RepToDonateToFaction = 0.5;
BitNodeMultipliers.AugmentationRepCost = 3;
BitNodeMultipliers.AugmentationMoneyCost = 3;
@ -268,6 +269,8 @@ export function initBitNodeMultipliers(p: IPlayer) {
BitNodeMultipliers.CompanyWorkMoney = 0.25;
BitNodeMultipliers.CrimeMoney = 0.25;
BitNodeMultipliers.HacknetNodeMoney = 0.25;
BitNodeMultipliers.HomeComputerRamCost = 1.5;
BitNodeMultipliers.PurchasedServerCost = 2;
break;
case 4: //The Singularity
BitNodeMultipliers.ServerMaxMoney = 0.15;

@ -1417,7 +1417,7 @@ Bladeburner.prototype.completeAction = function() {
break;
case ActionTypes["Hyperbolic Regeneration Chamber"]:
Player.regenerateHp(HrcHpGain);
this.stamina = Math.max(this.maxStamina, this.stamina + HrcStaminaGain); // TODO Turn this into a const and adjust value
this.stamina = Math.min(this.maxStamina, this.stamina + HrcStaminaGain);
this.startAction(this.action);
if (this.logging.general) {
this.log(`Rested in Hyperbolic Regeneration Chamber. Restored ${HrcHpGain} HP and gained ${HrcStaminaGain} stamina`);

@ -291,7 +291,15 @@ export let CONSTANTS: IMap<any> = {
** Added several new Research upgrades
** Reduced the amount of Scientific Research needed to unlock the Hi-Tech R&D Laboratory from 10k to 5k
** Energy Material requirement of the Software industry reduced from 1 to 0.5
** It is now slightly easier to increase the Software industry's production multiplier
** Industries now have a maximum number of allowed products, starting at 3. This can be increased through research.
** You can now see an approximation of how each material affects an industry's production multiplier by clicking the "?" help tip next to it
** Significantly changed the effects of the different employee positions. See updated descriptions
** Reduced the amount of money you gain from private investors
** Training employees is now 3x more effective
* Rebalanced BitNode-3 to make it slightly harder
* Bug Fix: Bladeburner's Hyperbolic Regeneration Chamber should no longer instantly refill all stamina
`
}

@ -24,6 +24,8 @@ import { Player } from "../Player";
import { numeralWrapper } from "../ui/numeralFormat";
import { Page, routing } from "../ui/navigationTracking";
import { calculateEffectWithFactors } from "../utils/calculateEffectWithFactors";
import { dialogBoxCreate } from "../../utils/DialogBox";
import { clearSelector } from "../../utils/uiHelpers/clearSelector";
import { Reviver,
@ -37,7 +39,6 @@ import { formatNumber, generateRandomString } from "../../utils/String
import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { isString } from "../../utils/helpers/isString";
import { KEY } from "../../utils/helpers/keyCodes";
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
import { removeElement } from "../../utils/uiHelpers/removeElement";
import { removeElementById } from "../../utils/uiHelpers/removeElementById";
import { yesNoBoxCreate,
@ -48,8 +49,7 @@ import { yesNoBoxCreate,
yesNoTxtInpBoxGetNoButton,
yesNoTxtInpBoxGetInput,
yesNoBoxClose,
yesNoTxtInpBoxClose,
yesNoBoxOpen } from "../../utils/YesNoBox";
yesNoTxtInpBoxClose } from "../../utils/YesNoBox";
// UI Related Imports
import React from "react";
@ -124,18 +124,6 @@ function Industry(params={}) {
[Locations.Volhaven]: 0
};
this.warehouses = { //Maps locations to warehouses. 0 if no warehouse at that location
[Locations.Aevum]: 0,
[Locations.Chonqing]: 0,
[Locations.Sector12]: new Warehouse({
loc:Locations.Sector12,
size: WarehouseInitialSize,
}),
[Locations.NewTokyo]: 0,
[Locations.Ishima]: 0,
[Locations.Volhaven]: 0
};
this.name = params.name ? params.name : 0;
this.type = params.type ? params.type : 0;
@ -183,6 +171,20 @@ function Industry(params={}) {
this.state = "START";
this.newInd = true;
this.warehouses = { //Maps locations to warehouses. 0 if no warehouse at that location
[Locations.Aevum]: 0,
[Locations.Chonqing]: 0,
[Locations.Sector12]: new Warehouse({
corp: params.corp,
industry: this,
loc: Locations.Sector12,
size: WarehouseInitialSize,
}),
[Locations.NewTokyo]: 0,
[Locations.Ishima]: 0,
[Locations.Volhaven]: 0
};
this.init();
}
@ -340,8 +342,8 @@ Industry.prototype.init = function() {
this.sciFac = 0.62;
this.advFac = 0.16;
this.hwFac = 0.25;
this.reFac = 0.1;
this.aiFac = 0.15;
this.reFac = 0.15;
this.aiFac = 0.18;
this.robFac = 0.05;
this.reqMats = {
"Hardware": 0.5,
@ -711,7 +713,7 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
for (var j = 0; j < this.prodMats.length; ++j) {
warehouse.materials[this.prodMats[j]].qty += (prod * producableFrac);
warehouse.materials[this.prodMats[j]].qlt =
(office.employeeProd[EmployeePositions.Engineer] / 100 +
(office.employeeProd[EmployeePositions.Engineer] / 90 +
Math.pow(this.sciResearch.qty, this.sciFac) +
Math.pow(warehouse.materials["AICores"].qty, this.aiFac) / 10e3);
}
@ -918,27 +920,30 @@ Industry.prototype.processProducts = function(marketCycles=1, corporation) {
//Create products
if (this.state === "PRODUCTION") {
for (var prodName in this.products) {
if (this.products.hasOwnProperty(prodName)) {
var prod = this.products[prodName];
if (!prod.fin) {
var city = prod.createCity, office = this.offices[city];
var total = office.employeeProd[EmployeePositions.Operations] +
office.employeeProd[EmployeePositions.Engineer] +
office.employeeProd[EmployeePositions.Management], ratio;
if (total === 0) {
ratio = 0;
} else {
ratio = office.employeeProd[EmployeePositions.Engineer] / total +
office.employeeProd[EmployeePositions.Operations] / total +
office.employeeProd[EmployeePositions.Management] / total;
}
prod.createProduct(marketCycles, ratio * Math.pow(total, 0.35));
if (prod.prog >= 100) {
prod.finishProduct(office.employeeProd, this);
}
break;
for (const prodName in this.products) {
const prod = this.products[prodName];
if (!prod.fin) {
const city = prod.createCity;
const office = this.offices[city];
// Designing/Creating a Product is based mostly off Engineers
const engrProd = office.employeeProd[EmployeePositions.Engineer];
const mgmtProd = office.employeeProd[EmployeePositions.Management];
const opProd = office.employeeProd[EmployeePositions.Operations];
const total = engrProd + mgmtProd + opProd;
if (total <= 0) { break; }
// Management is a multiplier for the production from Engineers
const mgmtFactor = 1 + (mgmtProd / (1.2 * total));
const progress = (Math.pow(engrProd, 0.34) + Math.pow(opProd, 0.2)) * mgmtFactor;
prod.createProduct(marketCycles, progress);
if (prod.prog >= 100) {
prod.finishProduct(office.employeeProd, this);
}
break;
}
}
}
@ -1147,33 +1152,38 @@ Industry.prototype.upgrade = function(upgrade, refs) {
}
}
//Returns how much of a material can be produced based of office productivity (employee stats)
// Returns how much of a material can be produced based of office productivity (employee stats)
Industry.prototype.getOfficeProductivity = function(office, params) {
var total = office.employeeProd[EmployeePositions.Operations] +
office.employeeProd[EmployeePositions.Engineer] +
office.employeeProd[EmployeePositions.Management], ratio;
if (total === 0) {
ratio = 0;
} else {
ratio = (office.employeeProd[EmployeePositions.Operations] / total) *
(office.employeeProd[EmployeePositions.Engineer] / total) *
(office.employeeProd[EmployeePositions.Management] / total);
ratio = Math.max(0.01, ratio); //Minimum ratio value if you have employees
}
const opProd = office.employeeProd[EmployeePositions.Operations];
const engrProd = office.employeeProd[EmployeePositions.Engineer];
const mgmtProd = office.employeeProd[EmployeePositions.Management]
const total = opProd + engrProd + mgmtProd;
if (total <= 0) { return 0; }
// Management is a multiplier for the production from Operations and Engineers
const mgmtFactor = 1 + (mgmtProd / (1.2 * total));
// For production, Operations is slightly more important than engineering
// Both Engineering and Operations have diminishing returns
const prod = (Math.pow(opProd, 0.4) + Math.pow(engrProd, 0.3)) * mgmtFactor;
// Generic multiplier for the production. Used for game-balancing purposes
const balancingMult = 0.05;
if (params && params.forProduct) {
return ratio * Math.pow(total, 0.25);
// Products are harder to create and therefore have less production
return 0.5 * balancingMult * prod;
} else {
return 2 * ratio * Math.pow(total, 0.35);
return balancingMult * prod;
}
}
//Returns a multiplier based on the office' 'Business' employees that affects sales
// Returns a multiplier based on the office' 'Business' employees that affects sales
Industry.prototype.getBusinessFactor = function(office) {
var ratioMult = 1;
if (office.employeeProd["total"] > 0) {
ratioMult = 1 + (office.employeeProd[EmployeePositions.Business] / office.employeeProd["total"]);
}
return ratioMult * Math.pow(1 + office.employeeProd[EmployeePositions.Business], 0.25);
const businessProd = 1 + office.employeeProd[EmployeePositions.Business];
return calculateEffectWithFactors(businessProd, 0.26, 10e3);
}
//Returns a set of multipliers based on the Industry's awareness, popularity, and advFac. This
@ -1399,7 +1409,7 @@ function Employee(params={}) {
//Returns the amount the employee needs to be paid
Employee.prototype.process = function(marketCycles=1, office) {
var gain = 0.001 * marketCycles,
var gain = 0.003 * marketCycles,
det = gain * Math.random();
this.age += gain;
this.exp += gain;
@ -1425,16 +1435,9 @@ Employee.prototype.process = function(marketCycles=1, office) {
this.eff += trainingEff;
}
//Weight based on how full office is
//Too many employees = more likely to decrease energy and happiness
var officeCapacityWeight = 0.5 * (office.employees.length / office.size - 0.5);
if (Math.random() < 0.5 - officeCapacityWeight) {
this.ene += det;
this.hap += det;
} else {
this.ene -= det;
this.hap -= det;
}
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;
@ -1640,18 +1643,16 @@ OfficeSpace.prototype.process = function(marketCycles=1, parentRefs) {
salaryPaid += salary;
}
this.calculateEmployeeProductivity(marketCycles, parentRefs);
this.calculateEmployeeProductivity(parentRefs);
return salaryPaid;
}
OfficeSpace.prototype.calculateEmployeeProductivity = function(marketCycles=1, parentRefs) {
OfficeSpace.prototype.calculateEmployeeProductivity = function(parentRefs) {
var company = parentRefs.corporation, industry = parentRefs.industry;
//Reset
for (const name in this.employeeProd) {
if (this.employeeProd.hasOwnProperty(name)) {
this.employeeProd[name] = 0;
}
this.employeeProd[name] = 0;
}
var total = 0;
@ -1977,19 +1978,19 @@ Corporation.prototype.getInvestment = function() {
switch (this.fundingRound) {
case 0: //Seed
percShares = 0.10;
roundMultiplier = 5;
roundMultiplier = 4;
break;
case 1: //Series A
percShares = 0.35;
roundMultiplier = 4;
roundMultiplier = 3;
break;
case 2: //Series B
percShares = 0.25;
roundMultiplier = 4;
roundMultiplier = 3;
break;
case 3: //Series C
percShares = 0.20;
roundMultiplier = 3.5;
roundMultiplier = 2.5;
break;
case 4:
return;

@ -164,11 +164,11 @@ export class Product {
//Calculate properties
var progrMult = this.prog / 100;
var engrRatio = employeeProd[EmployeePositions.Engineer] / employeeProd["total"],
mgmtRatio = employeeProd[EmployeePositions.Management] / employeeProd["total"],
rndRatio = employeeProd[EmployeePositions.RandD] / employeeProd["total"],
opsRatio = employeeProd[EmployeePositions.Operations] / employeeProd["total"],
busRatio = employeeProd[EmployeePositions.Business] / employeeProd["total"];
const engrRatio = employeeProd[EmployeePositions.Engineer] / employeeProd["total"];
const mgmtRatio = employeeProd[EmployeePositions.Management] / employeeProd["total"];
const rndRatio = employeeProd[EmployeePositions.RandD] / employeeProd["total"];
const opsRatio = employeeProd[EmployeePositions.Operations] / employeeProd["total"];
const busRatio = employeeProd[EmployeePositions.Business] / employeeProd["total"];
var designMult = 1 + (Math.pow(this.designCost, 0.1) / 100);
console.log("designMult: " + designMult);
var balanceMult = (1.2 * engrRatio) + (0.9 * mgmtRatio) + (1.3 * rndRatio) +

@ -8,6 +8,8 @@ import { ResearchMap } from "./ResearchMap";
import { IMap } from "../types";
import { numeralWrapper } from "../ui/numeralFormat";
interface IConstructorParams {
children?: Node[];
cost: number;
@ -83,7 +85,7 @@ export class Node {
children: childrenArray,
HTMLclass: htmlClass,
innerHTML: `<div id="${sanitizedName}-corp-research-click-listener" class="tooltip">` +
`${this.text}<br>${this.cost} Scientific Research` +
`${this.text}<br>${numeralWrapper.format(this.cost, "0,0")} Scientific Research` +
`<span class="tooltiptext">` +
`${research.desc}` +
`</span>` +

@ -5,16 +5,19 @@ import { numeralWrapper } from "../ui/numeralFormat";
import { Generic_fromJSON,
Generic_toJSON,
Reviver } from "../../utils/JSONReviver";
interface IConstructorParams {
loc?: string;
size?: number;
}
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
interface IParent {
getStorageMultiplier(): number;
}
interface IConstructorParams {
corp?: IParent;
industry?: IParent;
loc?: string;
size?: number;
}
export class Warehouse {
// Initiatizes a Warehouse object from a JSON save state.
static fromJSON(value: any): Warehouse {
@ -65,6 +68,10 @@ export class Warehouse {
AICores: new Material({name: "AI Cores"}),
RealEstate: new Material({name: "Real Estate"})
}
if (params.corp && params.industry) {
this.updateSize(params.corp, params.industry);
}
}
// Re-calculate how much space is being used by this Warehouse
@ -76,7 +83,7 @@ export class Warehouse {
if (MaterialSizes.hasOwnProperty(matName)) {
this.sizeUsed += (mat.qty * MaterialSizes[matName]);
if (mat.qty > 0) {
this.breakdown += (matName + ": " + numeralWrapper.format(mat.qty * MaterialSizes[matName], "0,0") + "<br>");
this.breakdown += (matName + ": " + numeralWrapper.format(mat.qty * MaterialSizes[matName], "0,0.0") + "<br>");
}
}
}
@ -86,9 +93,13 @@ export class Warehouse {
}
updateSize(corporation: IParent, industry: IParent) {
this.size = (this.level * 100)
* corporation.getStorageMultiplier()
* industry.getStorageMultiplier();
try {
this.size = (this.level * 100)
* corporation.getStorageMultiplier()
* industry.getStorageMultiplier();
} catch(e) {
exceptionAlert(e);
}
}
// Serialize the current object to a JSON save state.

@ -14,6 +14,9 @@ export class CityTabs extends BaseReactComponent {
if (props.city == null) {
throw new Error(`CityTabs component constructed without 'city' property`)
}
if (props.cityStateSetter == null) {
throw new Error(`CityTabs component constructed without 'cityStateSetter' property`)
}
super(props);
}
@ -46,7 +49,8 @@ export class CityTabs extends BaseReactComponent {
}
// Tab to "Expand into new City"
const newCityOnClick = this.eventHandler().createNewCityPopup.bind(this.eventHandler(), division);
const newCityOnClick = this.eventHandler().createNewCityPopup.bind(this.eventHandler(), division, this.props.cityStateSetter);
tabs.push(this.renderTab({
current: false,
key: "Expand into new City",

@ -17,6 +17,8 @@ import { Industries,
IndustryDescriptions,
IndustryResearchTrees } from "../IndustryData";
import { Product } from "../Product";
import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat";
@ -109,7 +111,7 @@ export class CorporationEventHandler {
}
}
});
var confirmButton = createElement("a", {
var confirmButton = createElement("button", {
class:"a-link-button", innerText:"Bribe", display:"inline-block",
clickListener:()=>{
var money = moneyInput.value == null || moneyInput.value == "" ? 0 : parseFloat(moneyInput.value);
@ -181,7 +183,7 @@ export class CorporationEventHandler {
}
}
});
var confirmBtn = createElement("a", {
var confirmBtn = createElement("button", {
class:"a-link-button", innerText:"Buy shares", display:"inline-block",
clickListener: () => {
var shares = Math.round(input.value);
@ -234,12 +236,12 @@ export class CorporationEventHandler {
"produce this product and all of its existing stock will be " +
"removed and left unsold",
});
const confirmBtn = createElement("a", {
const confirmBtn = createElement("button", {
class:"a-link-button",innerText:"Discontinue",
clickListener:()=>{
clickListener: () => {
industry.discontinueProduct(product, parentRefs);
removeElementById(popupId);
this.corp.rerender();
this.rerender();
return false;
}
});
@ -294,7 +296,7 @@ export class CorporationEventHandler {
placeholder:"Export amount / s"
});
const exportBtn = createElement("a", {
const exportBtn = createElement("button", {
class: "std-button", display:"inline-block", innerText:"Export",
clickListener: () => {
const industryName = getSelectText(industrySelector);
@ -473,7 +475,7 @@ export class CorporationEventHandler {
}
});
issueBtn = createElement("a", {
issueBtn = createElement("button", {
class: "std-button",
display: "inline-block",
innerText: "Issue New Shares",
@ -529,7 +531,7 @@ export class CorporationEventHandler {
}
// Create a popup that lets the player limit the production of a product
createLimitProductProdutionPopup(product) {
createLimitProductProdutionPopup(product, city) {
const popupId = "cmpy-mgmt-limit-product-production-popup";
const txt = createElement("p", {
innerText:"Enter a limit to the amount of this product you would " +
@ -543,7 +545,7 @@ export class CorporationEventHandler {
if (e.keyCode === KEY.ENTER) { confirmBtn.click(); }
}
});
confirmBtn = createElement("a", {
confirmBtn = createElement("button", {
class: "std-button",
display:"inline-block",
innerText:"Limit production",
@ -585,7 +587,7 @@ export class CorporationEventHandler {
const txt = createElement("p", {
innerHTML: popupText,
});
const designCity = createElement("select");
const designCity = createElement("select", { margin: "5px" });
for (const cityName in division.offices) {
if (division.offices[cityName] instanceof OfficeSpace) {
designCity.add(createElement("option", {
@ -603,18 +605,26 @@ export class CorporationEventHandler {
productNamePlaceholder = "Property Name";
}
var productNameInput = createElement("input", {
margin: "5px",
placeholder: productNamePlaceholder,
});
var lineBreak1 = createElement("br");
var designInvestInput = createElement("input", {
margin: "5px",
placeholder: "Design investment",
type: "number",
placeholder: "Design investment"
});
let confirmBtn;
var marketingInvestInput = createElement("input", {
margin: "5px",
placeholder: "Marketing investment",
type: "number",
placeholder: "Marketing investment"
onkeyup: (e) => {
e.preventDefault();
if (e.keyCode === KEY.ENTER) { confirmBtn.click(); }
}
});
const confirmBtn = createElement("a", {
confirmBtn = createElement("button", {
class: "std-button",
innerText: "Develop Product",
clickListener: () => {
@ -637,17 +647,19 @@ export class CorporationEventHandler {
designCost: designInvest,
advCost: marketingInvest,
});
if (division.products[product.name] instanceof Product) {
dialogBoxCreate(`You already have a product with this name!`);
return;
}
this.corp.funds = this.corp.funds.minus(designInvest + marketingInvest);
division.products[product.name] = product;
removeElementById(popupId);
}
this.rerender();
//this.updateUIContent();
//this.displayDivisionContent(division, city);
return false;
}
})
const cancelBtn = createPopupCloseButton(popupid, {
const cancelBtn = createPopupCloseButton(popupId, {
class: "std-button",
innerText: "Cancel",
});
@ -741,7 +753,8 @@ export class CorporationEventHandler {
}
// Create a popup that lets the player expand into a new city (for the current industry)
createNewCityPopup(division) {
// The 'cityStateSetter' arg is a function that sets the UI's 'city' state property
createNewCityPopup(division, cityStateSetter) {
const popupId = "cmpy-mgmt-expand-city-popup";
const text = createElement("p", {
innerText: "Would you like to expand into a new city by opening an office? " +
@ -757,7 +770,7 @@ export class CorporationEventHandler {
}
}
const confirmBtn = createElement("a", {
const confirmBtn = createElement("button", {
class:"std-button",
display:"inline-block",
innerText: "Confirm",
@ -772,9 +785,11 @@ export class CorporationEventHandler {
loc: city,
size: OfficeInitialSize,
});
this.corp.displayDivisionContent(division, city);
}
cityStateSetter(city);
removeElementById(popupId);
this.rerender();
return false;
}
});
@ -836,15 +851,17 @@ export class CorporationEventHandler {
} else {
this.corp.funds = this.corp.funds.minus(IndustryStartingCosts[ind]);
var newInd = new Industry({
name:newDivisionName,
type:ind,
corp: this.corp,
name: newDivisionName,
type: ind,
});
this.corp.divisions.push(newInd);
// this.corp.updateUIHeaderTabs();
// this.corp.selectHeaderTab(headerTabs[headerTabs.length-2]);
// Set routing to the new division so that the UI automatically switches to it
this.routing.routeTo(newDivisionName);
removeElementById("cmpy-mgmt-expand-industry-popup");
this.rerender();
// this.corp.displayDivisionContent(newInd, Locations.Sector12);
}
return false;
}
@ -855,14 +872,14 @@ export class CorporationEventHandler {
innerText: "Cancel",
});
//Make an object to keep track of what industries you're already in
// Make an object to keep track of what industries you're already in
const ownedIndustries = {};
for (let i = 0; i < this.corp.divisions.length; ++i) {
ownedIndustries[this.corp.divisions[i].type] = true;
}
//Add industry types to selector
//Have Agriculture be first as recommended option
// Add industry types to selector
// Have Agriculture be first as recommended option
if (!ownedIndustries["Agriculture"]) {
selector.add(createElement("option", {
text:Industries["Agriculture"], value:"Agriculture"
@ -933,6 +950,7 @@ export class CorporationEventHandler {
mat.buy = parseFloat(input.value);
if (isNaN(mat.buy)) {mat.buy = 0;}
removeElementById(purchasePopupId);
this.rerender();
return false;
}
}
@ -942,6 +960,7 @@ export class CorporationEventHandler {
clickListener: () => {
mat.buy = 0;
removeElementById(purchasePopupId);
this.rerender();
return false;
}
});
@ -979,7 +998,7 @@ export class CorporationEventHandler {
type: "number",
onkeyup: (e) => {
e.preventDefault();
bulkPurchaseUpdateCostTxt();
updateBulkPurchaseText();
if (e.keyCode === KEY.ENTER) {bulkPurchaseConfirmBtn.click();}
}
});
@ -1010,6 +1029,7 @@ export class CorporationEventHandler {
elems.push(bulkPurchaseInfo);
elems.push(bulkPurchaseCostTxt);
elems.push(bulkPurchaseInput);
elems.push(bulkPurchaseConfirmBtn);
}
createPopup(purchasePopupId, elems);
@ -1129,7 +1149,7 @@ export class CorporationEventHandler {
}
// Create a popup that lets the player manage sales of the product
createSellProductPopup(product) {
createSellProductPopup(product, city) {
const popupId = "cmpy-mgmt-sell-product-popup";
const txt = createElement("p", {
innerHTML:"Enter the maximum amount of " + product.name + " you would like " +
@ -1150,15 +1170,17 @@ export class CorporationEventHandler {
});
let confirmBtn;
const inputQty = createElement("input", {
margin: "5px 0px 5px 0px",
placeholder: "Sell amount",
type: "text",
value:product.sllman[city][1] ? product.sllman[city][1] : null,
value: product.sllman[city][1] ? product.sllman[city][1] : null,
onkeyup: (e) => {
e.preventDefault();
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
}
});
const inputPx = createElement("input", {
margin: "5px 0px 5px 0px",
placeholder: "Sell price",
type: "text",
value: product.sCost ? product.sCost : null,
@ -1167,7 +1189,7 @@ export class CorporationEventHandler {
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
}
});
confirmBtn = createElement("a", {
confirmBtn = createElement("button", {
class: "std-button",
innerText: "Confirm",
clickListener: () => {
@ -1276,7 +1298,7 @@ export class CorporationEventHandler {
}
}
});
const confirmBtn = createElement("a", {
const confirmBtn = createElement("button", {
class:"a-link-button", innerText:"Sell shares", display:"inline-block",
clickListener:()=>{
var shares = Math.round(input.value);
@ -1355,7 +1377,7 @@ export class CorporationEventHandler {
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
}
});
confirmBtn = createElement("a", {
confirmBtn = createElement("button", {
class: "std-button",
innerText: "Throw Party",
clickListener:()=>{
@ -1366,20 +1388,20 @@ export class CorporationEventHandler {
if (this.corp.funds.lt(totalCost)) {
dialogBoxCreate("You don't have enough company funds to throw this.corp party!");
} else {
this.corp.funds = this.funds.minus(totalCost);
this.corp.funds = this.corp.funds.minus(totalCost);
var mult;
for (let fooit = 0; fooit < office.employees.length; ++fooit) {
mult = office.employees[fooit].throwParty(input.value);
}
dialogBoxCreate("You threw a party for the office! The morale and happiness " +
"of each employee increased by " + formatNumber((mult-1) * 100, 2) + "%.");
"of each employee increased by " + numeralWrapper.formatPercentage((mult-1)));
removeElementById(popupId);
}
}
return false;
}
});
const cancelBtn = createPopupCloseButton(popupId, { innerText: "Cancel" });
const cancelBtn = createPopupCloseButton(popupId, { class: "std-button", innerText: "Cancel" });
createPopup(popupId, [txt, totalCostTxt, input, confirmBtn, cancelBtn]);
input.focus();
@ -1420,7 +1442,7 @@ export class CorporationEventHandler {
});
const text2 = createElement("p", { innerText: "Upgrade size: " });
const confirmBtn = createElement("a", {
const confirmBtn = createElement("button", {
class: this.corp.funds.lt(upgradeCost) ? "a-link-button-inactive" : "a-link-button",
display:"inline-block", margin:"4px", innerText:"by 3",
tooltip:numeralWrapper.format(upgradeCost, "$0.000a"),
@ -1437,7 +1459,7 @@ export class CorporationEventHandler {
return false;
}
});
const confirmBtn15 = createElement("a", {
const confirmBtn15 = createElement("button", {
class: this.corp.funds.lt(upgradeCost15) ? "a-link-button-inactive" : "a-link-button",
display:"inline-block", margin:"4px", innerText:"by 15",
tooltip:numeralWrapper.format(upgradeCost15, "$0.000a"),
@ -1454,7 +1476,7 @@ export class CorporationEventHandler {
return false;
}
});
const confirmBtnMax = createElement("a", {
const confirmBtnMax = createElement("button", {
class:this.corp.funds.lt(upgradeCostMax) ? "a-link-button-inactive" : "a-link-button",
display:"inline-block", margin:"4px", innerText:"by MAX (" + maxNum*OfficeInitialSize + ")",
tooltip:numeralWrapper.format(upgradeCostMax, "$0.000a"),
@ -1484,6 +1506,8 @@ export class CorporationEventHandler {
dialogBoxCreate("You do not have enough funds to do this!");
} else {
division.warehouses[city] = new Warehouse({
corp: corp,
industry: division,
loc: city,
size: WarehouseInitialSize,
});

@ -15,6 +15,8 @@ export class IndustryOffice extends BaseReactComponent {
super(props);
this.state = {
city: "",
division: "",
employeeManualAssignMode: false,
employee: null, // Reference to employee being referenced if in Manual Mode
numEmployees: 0,
@ -30,6 +32,17 @@ export class IndustryOffice extends BaseReactComponent {
this.updateEmployeeCount(); // This function validates division and office refs
}
resetEmployeeCount() {
this.state.numEmployees = 0;
this.state.numOperations = 0;
this.state.numEngineers = 0;
this.state.numBusiness = 0;
this.state.numManagement = 0;
this.state.numResearch = 0;
this.state.numUnassigned = 0;
this.state.numTraining = 0;
}
updateEmployeeCount() {
const division = this.routing().currentDivision;
if (division == null) {
@ -40,6 +53,13 @@ export class IndustryOffice extends BaseReactComponent {
throw new Error(`Current City (${this.props.currentCity}) for UI does not have an OfficeSpace object`);
}
// If we're in a new city, we have to reset the state
if (division.name !== this.state.division || this.props.currentCity !== this.state.city) {
this.resetEmployeeCount();
this.state.division = division.name;
this.state.city = this.props.currentCity;
}
// Calculate how many NEW emplyoees we need to account for
const currentNumEmployees = office.employees.length;
const newEmployees = currentNumEmployees - this.state.numEmployees;
@ -151,6 +171,7 @@ export class IndustryOffice extends BaseReactComponent {
--this.state.numUnassigned;
office.assignEmployeeToJob(to);
office.calculateEmployeeProductivity({ corporation: this.corp(), industry:division });
this.corp().rerender();
}
@ -194,6 +215,7 @@ export class IndustryOffice extends BaseReactComponent {
++this.state.numUnassigned;
office.unassignEmployeeFromJob(from);
office.calculateEmployeeProductivity({ corporation: this.corp(), industry:division });
this.corp().rerender();
}
@ -283,36 +305,40 @@ export class IndustryOffice extends BaseReactComponent {
<p>Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}</p>
{
vechain &&
<div>
<p className={"tooltip"}>
<p className={"tooltip"} style={{display: "block"}}>
Material Production: {numeralWrapper.format(division.getOfficeProductivity(office), "0.000")}
<span className={"tooltiptext"}>
The base amount of material this office can produce. Does not include
production multipliers from upgrades and materials. This value is based off
the productivity of your Operations, Engineering, and Management employees
</span>
</p><br />
<p className={"tooltip"}>
</p>
}
{
vechain &&
<p className={"tooltip"} style={{display: "block"}}>
Product Production: {numeralWrapper.format(division.getOfficeProductivity(office, {forProduct:true}), "0.000")}
<span className={"tooltiptext"}>
The base amount of any given Product this office can produce. Does not include
production multipliers from upgrades and materials. This value is based off
the productivity of your Operations, Engineering, and Management employees
</span>
</p><br />
<p className={"tooltip"}>
Business Multiplier: x" ${numeralWrapper.format(division.getBusinessFactor(office), "0.000")}
</p>
}
{
vechain &&
<p className={"tooltip"} style={{display: "block"}}>
Business Multiplier: x{numeralWrapper.format(division.getBusinessFactor(office), "0.000")}
<span className={"tooltiptext"}>
The effect this office's 'Business' employees has on boosting sales
</span>
</p><br />
</div>
</p>
}
<h2 className={"tooltip"} style={positionHeaderStyle}>
{EmployeePositions.Operations} ({this.state.numOperations})
<span className={"tooltiptext"}>
Manages supply chain operations. Improves production.
Manages supply chain operations. Improves the amount of Materials and Products you produce.
</span>
</h2>
<button className={assignButtonClass} onClick={operationAssignButtonOnClick}>+</button>
@ -322,7 +348,9 @@ export class IndustryOffice extends BaseReactComponent {
<h2 className={"tooltip"} style={positionHeaderStyle}>
{EmployeePositions.Engineer} ({this.state.numEngineers})
<span className={"tooltiptext"}>
Develops and maintains products and production systems. Improves production.
Develops and maintains products and production systems. Increases the quality of
everything you produce. Also increases the amount you produce (not as much
as Operations, however)
</span>
</h2>
<button className={assignButtonClass} onClick={engineerAssignButtonOnClick}>+</button>
@ -332,7 +360,7 @@ export class IndustryOffice extends BaseReactComponent {
<h2 className={"tooltip"} style={positionHeaderStyle}>
{EmployeePositions.Business} ({this.state.numBusiness})
<span className={"tooltiptext"}>
Handles sales and finances. Improves sales.
Handles sales and finances. Improves the amount of Materials and Products you can sell.
</span>
</h2>
<button className={assignButtonClass} onClick={businessAssignButtonOnClick}>+</button>
@ -342,7 +370,8 @@ export class IndustryOffice extends BaseReactComponent {
<h2 className={"tooltip"} style={positionHeaderStyle}>
{EmployeePositions.Management} ({this.state.numManagement})
<span className={"tooltiptext"}>
Leads and oversees employees and office operations. Improves production.
Leads and oversees employees and office operations. Improves the effectiveness of
Engineer and Operations employees
</span>
</h2>
<button className={assignButtonClass} onClick={managementAssignButtonOnClick}>+</button>
@ -398,27 +427,36 @@ export class IndustryOffice extends BaseReactComponent {
for (let i = 0; i < office.employees.length; ++i) {
if (name === office.employees[i].name) {
this.state.employee = office.employees[i];
break;
}
}
corp.rerender();
}
// Employee Positions Selector
const emp = this.state.employee;
let employeePositionSelectorInitialValue = null;
const employeePositions = [];
const positionNames = Object.values(EmployeePositions);
for (let i = 0; i < positionNames.length; ++i) {
employeePositions.push(<option key={positionNames[i]}>{positionNames[i]}</option>);
employeePositions.push(<option key={positionNames[i]} value={positionNames[i]}>{positionNames[i]}</option>);
if (emp != null && emp.pos === positionNames[i]) {
employeePositionSelectorInitialValue = positionNames[i];
}
}
const employeePositionSelectorOnChange = (e) => {
const pos = getSelectText(e.target);
this.state.employee.pos = pos;
this.resetEmployeeCount();
corp.rerender();
}
// Numeraljs formatter
const nf = "0.000";
// Employee stats (after applying multipliers)
const emp = this.state.employee;
const effCre = emp ? emp.cre * corp.getEmployeeCreMultiplier() * division.getEmployeeCreMultiplier() : 0;
const effCha = emp ? emp.cha * corp.getEmployeeChaMultiplier() * division.getEmployeeChaMultiplier() : 0;
const effInt = emp ? emp.int * corp.getEmployeeIntMultiplier() * division.getEmployeeIntMultiplier() : 0;
@ -436,6 +474,9 @@ export class IndustryOffice extends BaseReactComponent {
</button>
<div style={employeeInfoDivStyle}>
<select onChange={employeeSelectorOnChange}>
{employees}
</select>
{
this.state.employee != null &&
<p>
@ -462,15 +503,11 @@ export class IndustryOffice extends BaseReactComponent {
}
{
this.state.employee != null &&
<select onChange={employeePositionSelectorOnChange}>
<select onChange={employeePositionSelectorOnChange} value={employeePositionSelectorInitialValue}>
{employeePositions}
</select>
}
</div>
<select onChange={employeeSelectorOnChange}>
{employees}
</select>
</div>
)
}
@ -495,7 +532,6 @@ export class IndustryOffice extends BaseReactComponent {
}
}
const hireEmployeeButtonOnClick = () => {
office.findEmployees({ corporation: corp, industry: division });
}

@ -8,6 +8,7 @@ import { Industries } from "../IndustryData";
import { IndustryUpgrades } from "../IndustryUpgrades";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
export class IndustryOverview extends BaseReactComponent {
renderMakeProductButton() {
@ -109,6 +110,16 @@ export class IndustryOverview extends BaseReactComponent {
const profitStr = `Profit: ${numeralWrapper.formatMoney(profit)} / s`;
const productionMultHelpTipOnClick = () => {
// Wrapper for createProgressBarText()
// Converts the industry's "effectiveness factors"
// into a graphic (string) depicting how high that effectiveness is
function convertEffectFacToGraphic(fac) {
return createProgressBarText({
progress: fac,
totalTicks: 20,
});
}
dialogBoxCreate("Owning Hardware, Robots, AI Cores, and Real Estate " +
"can boost your Industry's production. The effect these " +
"materials have on your production varies between Industries. " +
@ -118,7 +129,13 @@ export class IndustryOverview extends BaseReactComponent {
"the individual production multiplier of each of its office locations. " +
"This production multiplier is applied to each office. Therefore, it is " +
"beneficial to expand into new cities as this can greatly increase the " +
"production multiplier of your entire Division.");
"production multiplier of your entire Division.<br><br>" +
"Below are approximations for how effective each material is at boosting " +
"this industry's production multiplier (Bigger bars = more effective):<br><br>" +
`Hardware:&nbsp;&nbsp;&nbsp; ${convertEffectFacToGraphic(division.hwFac)}<br>` +
`Robots:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ${convertEffectFacToGraphic(division.robFac)}<br>` +
`AI Cores:&nbsp;&nbsp;&nbsp; ${convertEffectFacToGraphic(division.aiFac)}<br>` +
`Real Estate: ${convertEffectFacToGraphic(division.reFac)}`);
}
return (
@ -129,15 +146,15 @@ export class IndustryOverview extends BaseReactComponent {
{popularity} <br />
{
(advertisingInfo !== false) &&
<p className={"tooltip"}>Advertising Multiplier: {numeralWrapper.format(totalAdvertisingFac, "0.000")}
<p className={"tooltip"}>Advertising Multiplier: x{numeralWrapper.format(totalAdvertisingFac, "0.000")}
<span className={"tooltiptext cmpy-mgmt-advertising-info"}>
Total multiplier for this industrys sales due to its awareness and popularity
<br />
Awareness Bonus: x{formatNumber(Math.pow(awarenessFac, 0.85), 3)}
Awareness Bonus: x{numeralWrapper.format(Math.pow(awarenessFac, 0.85), "0.000")}
<br />
Popularity Bonus: x{formatNumber(Math.pow(popularityFac, 0.85), 3)}
Popularity Bonus: x{numeralWrapper.format(Math.pow(popularityFac, 0.85), "0.000")}
<br />
Ratio Multiplier: x{formatNumber(Math.pow(ratioFac, 0.85), 3)}
Ratio Multiplier: x{numeralWrapper.format(Math.pow(ratioFac, 0.85), "0.000")}
</span>
</p>
}
@ -157,7 +174,7 @@ export class IndustryOverview extends BaseReactComponent {
<div className={"help-tip"} onClick={productionMultHelpTipOnClick}>?</div>
<br /> <br />
<p className={"tooltip"}>
Scientific Research: {numeralWrapper.format(division.sciResearch.qty, "0.000")}
Scientific Research: {numeralWrapper.format(division.sciResearch.qty, "0.000a")}
<span className={"tooltiptext"}>
Scientific Research increases the quality of the materials and
products that you produce.
@ -252,7 +269,7 @@ export class IndustryOverview extends BaseReactComponent {
{
division.makesProducts &&
{makeProductButton}
makeProductButton
}
</div>
)

@ -1,38 +1,50 @@
// 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 { BaseReactComponent } from "./BaseReactComponent";
import { Material } from "../Material";
import { Product } from "../Product";
import { Material } from "../Material";
import { Product } from "../Product";
import { Warehouse,
WarehouseInitialCost,
WarehouseUpgradeBaseCost } from "../Corporation";
WarehouseUpgradeBaseCost,
ProductProductionCostRatio } from "../Corporation";
import { numeralWrapper } from "../../ui/numeralFormat";
import { numeralWrapper } from "../../ui/numeralFormat";
import { isString } from "../../../utils/helpers/isString";
import { isString } from "../../../utils/helpers/isString";
// Creates the UI for a single Product type
function ProductComponent(props) {
const corp = props.corp;
const division = props.division;
const warehouse = props.warehouse;
const city = props.city;
const product = props.product;
const eventHandler = props.eventHandler;
const nf = "0.000"; // Numeraljs formatter
// Numeraljs formatters
const nf = "0.000";
const nfB = "0.000a"; // For numbers that might be big
const hasUpgradeDashboard = division.hasResearch("uPgrade: Dashboard");
// Total product gain = production - sale
const totalGain = totalGain = product.data[city][1] - product.data[city][2];
const totalGain = product.data[city][1] - product.data[city][2];
// Sell button
const sellButtonText = product.sllman[city][1] === -1
? "Sell (" + numeralWrapper.format(product.data[city][2], nf) + "/MAX)"
: "Sell (" + numeralWrapper.format(product.data[city][2], nf) + "/" + numeralWrapper.format(product.sllman[city][1], nf) + ")";
let sellButtonText;
if (product.sllman[city][0]) {
if (isString(product.sllman[city][1])) {
sellButtonText = `Sell (${numeralWrapper.format(product.data[city][2], nfB)}/${product.sllman[city][1]})`;
} else {
sellButtonText = `Sell (${numeralWrapper.format(product.data[city][2], nfB)}/${numeralWrapper.format(product.sllman[city][1], nfB)})`;
}
} else {
sellButtonText = "Sell (0.000/0.000)";
}
if (product.sCost) {
if (isString(product.sCost)) {
sellButtonText += (" @ " + product.sCost);
@ -40,14 +52,14 @@ function ProductComponent(props) {
sellButtonText += (" @ " + numeralWrapper.format(product.sCost, "$0.000a"));
}
}
const sellButtonOnClick = eventHandler.createSellProductPopup.bind(eventHandler, product);
const sellButtonOnClick = eventHandler.createSellProductPopup.bind(eventHandler, product, city);
// Limit Production button
const limitProductionButtonText = "Limit Production";
if (product.prdman[city][0]) {
limitProductionButtonText += " (" + numeralWrapper.format(product.prdman[city][1], nf) + ")";
}
const limitProductionButtonOnClick = eventHandler.createLimitProductProdutionPopup.bind(eventHandler, product);
const limitProductionButtonOnClick = eventHandler.createLimitProductProdutionPopup.bind(eventHandler, product, city);
// Discontinue Button
const discontinueButtonOnClick = eventHandler.createDiscontinueProductPopup.bind(eventHandler, product);
@ -56,8 +68,8 @@ function ProductComponent(props) {
if (!product.fin) {
if (hasUpgradeDashboard) {
return (
<div className={"cmpy-mgmt-warehouse-product-div"}>
<p>Designing {product.name}...</p>
<div className={"cmpy-mgmt-warehouse-product-div"} key={product.name}>
<p>Designing {product.name}...</p><br />
<p>{numeralWrapper.format(product.prog, "0.00")}% complete</p>
<br />
@ -76,8 +88,8 @@ function ProductComponent(props) {
)
} else {
return (
<div className={"cmpy-mgmt-warehouse-product-div"}>
<p>Designing {product.name}...</p>
<div className={"cmpy-mgmt-warehouse-product-div"} key={product.name}>
<p>Designing {product.name}...</p><br />
<p>{numeralWrapper.format(product.prog, "0.00")}% complete</p>
</div>
);
@ -87,13 +99,13 @@ function ProductComponent(props) {
return (
<div className={"cmpy-mgmt-warehouse-product-div"} key={props.key}>
<p className={"tooltip"}>
{product.name}: {numeralWrapper.format(product.data[city][0], nf)} ({numeralWrapper.format(totalGain, nf)}/s)
{product.name}: {numeralWrapper.format(product.data[city][0], nfB)} ({numeralWrapper.format(totalGain, nfB)}/s)
<span className={"tooltiptext"}>
Prod: {numeralWrapper.format(product.data[city][1], nf)}/s
Prod: {numeralWrapper.format(product.data[city][1], nfB)}/s
<br />
Sell: {numeralWrapper.format(product.data[city][2], nf)} /s
Sell: {numeralWrapper.format(product.data[city][2], nfB)} /s
</span>
</p>
</p><br />
<p className={"tooltip"}>
Rating: {numeralWrapper.format(product.rat, nf)}
<span className={"tooltiptext"}>
@ -119,13 +131,13 @@ function ProductComponent(props) {
}
</span>
</p>
</p><br />
<p className={"tooltip"}>
Est. Production Cost: {numeralWrapper.formatMoney(product.pCost / ProductProductionCostRatio)}
<span className={"tooltiptext"}>
An estimate of the material cost it takes to create this Product.
</span>
</p>
</p><br />
<p className={"tooltip"}>
Est. Market Price: {numeralWrapper.formatMoney(product.pCost + product.rat / product.mku)}
<span className={"tooltiptext"}>
@ -161,6 +173,7 @@ function MaterialComponent(props) {
// Numeraljs formatter
const nf = "0.000";
const nfB = "0.000a"; // For numbers that might be biger
// Total gain or loss of this material (per second)
const totalGain = mat.buy + mat.prd + mat.imp - mat.sll - mat.totalExp;
@ -190,8 +203,12 @@ function MaterialComponent(props) {
// Sell material button
let sellButtonText;
if (mat.sllman[0]) {
sellButtonText = (mat.sllman[1] === -1 ? "Sell (" + numeralWrapper.format(mat.sll, nf) + "/MAX)" :
"Sell (" + numeralWrapper.format(mat.sll, nf) + "/" + numeralWrapper.format(mat.sllman[1], nf) + ")");
if (isString(mat.sllman[1])) {
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${mat.sllman[1]})`
} else {
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${numeralWrapper.format(mat.sllman[1], nf)})`;
}
if (mat.sCost) {
if (mat.marketTa1) {
sellButtonText += " @ " + numeralWrapper.formatMoney(mat.bCost + markupLimit);
@ -214,13 +231,13 @@ function MaterialComponent(props) {
<div className={"cmpy-mgmt-warehouse-material-div"} key={props.key}>
<div style={{display: "inline-block"}}>
<p className={"tooltip"}>
{mat.name}: {numeralWrapper.format(mat.qty, nf)} ({numeralWrapper.format(totalGain, nf)}/s)
{mat.name}: {numeralWrapper.format(mat.qty, nfB)} ({numeralWrapper.format(totalGain, nfB)}/s)
<span className={"tooltiptext"}>
Buy: {numeralWrapper.format(mat.buy, nf)} <br />
Prod: {numeralWrapper.format(mat.prd, nf)} <br />
Sell: {numeralWrapper.format(mat.sll, nf)} <br />
Export: {numeralWrapper.format(mat.totalExp, nf)} <br />
Import: {numeralWrapper.format(mat.imp, nf)}
Buy: {numeralWrapper.format(mat.buy, nfB)} <br />
Prod: {numeralWrapper.format(mat.prd, nfB)} <br />
Sell: {numeralWrapper.format(mat.sll, nfB)} <br />
Export: {numeralWrapper.format(mat.totalExp, nfB)} <br />
Import: {numeralWrapper.format(mat.imp, nfB)}
{
corp.unlockUpgrades[2] === 1 && <br />
}
@ -244,7 +261,7 @@ function MaterialComponent(props) {
</span>
</p> <br />
<p className={"tooltip"}>
Quality: {numeralWrapper.format(mat.qlt, "0.00")}
Quality: {numeralWrapper.format(mat.qlt, "0.00a")}
<span className={"tooltiptext"}>
The quality of your material. Higher quality will lead to more sales
</span>
@ -370,7 +387,8 @@ export class IndustryWarehouse extends BaseReactComponent {
// Smart Supply Checkbox
const smartSupplyCheckboxId = "cmpy-mgmt-smart-supply-checkbox";
const smartSupplyOnChange = (e) => {
warehouse.smartSupplyEnabled = e.target.value;
warehouse.smartSupplyEnabled = e.target.checked;
corp.rerender();
}
// Materials that affect Production multiplier
@ -409,14 +427,15 @@ export class IndustryWarehouse extends BaseReactComponent {
if (division.makesProducts && Object.keys(division.products).length > 0) {
for (const productName in division.products) {
if (division.products[productName] instanceof Product) {
products.push({
products.push(ProductComponent({
city: this.props.currentCity,
corp: corp,
division: division,
eventHandler: this.eventHandler(),
key: productName,
product: division.products[productName],
warehouse: warehouse,
})
}));
}
}
}
@ -425,9 +444,7 @@ export class IndustryWarehouse extends BaseReactComponent {
<div className={"cmpy-mgmt-warehouse-panel"}>
<p className={"tooltip"} style={sizeUsageStyle}>
Storage: {numeralWrapper.format(warehouse.sizeUsed, "0.000")} / {numeralWrapper.format(warehouse.size, "0.000")}
<span className={"tooltiptext"}>
{warehouse.breakdown}
</span>
<span className={"tooltiptext"} dangerouslySetInnerHTML={{__html: warehouse.breakdown}}></span>
</p>
<button className={upgradeWarehouseClass} onClick={upgradeWarehouseOnClick}>
@ -454,7 +471,7 @@ export class IndustryWarehouse extends BaseReactComponent {
id={smartSupplyCheckboxId}
onChange={smartSupplyOnChange}
style={{margin: "3px"}}
value={warehouse.smartSupplyEnabled}
checked={warehouse.smartSupplyEnabled}
/>
</div>
}
@ -481,9 +498,11 @@ export class IndustryWarehouse extends BaseReactComponent {
return this.renderWarehouseUI();
} else {
return (
<button className={"std-button"} onClick={newWarehouseOnClick}>
Purchase Warehouse ({numeralWrapper.formatMoney(WarehouseInitialCost)})
</button>
<div className={"cmpy-mgmt-warehouse-panel"}>
<button className={"std-button"} onClick={newWarehouseOnClick}>
Purchase Warehouse ({numeralWrapper.formatMoney(WarehouseInitialCost)})
</button>
</div>
)
}
}

@ -23,6 +23,15 @@ export class MainPanel extends BaseReactComponent {
}
}
// We can pass this setter to child components
changeCityState(newCity) {
if (Object.values(Cities).includes(newCity)) {
this.state.city = newCity;
} else {
console.error(`Tried to change MainPanel's city state to an invalid city: ${newCity}`);
}
}
// Determines what UI content to render based on routing
renderContent() {
if (this.routing().isOnOverviewPage()) {
@ -68,11 +77,13 @@ export class MainPanel extends BaseReactComponent {
}
}
}
const cityTabs = (
<CityTabs
{...this.props}
city={this.state.city}
onClicks={onClicks}
cityStateSetter={this.changeCityState.bind(this)}
/>
)

@ -64,7 +64,7 @@ export class Overview extends BaseReactComponent {
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>" +
"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") +
"<span class='tooltiptext'>" +
`Outstanding Shares: ${numeralWrapper.format(this.corp().issuedShares, "0.000a")}<br>` +

@ -1,3 +1,5 @@
import { Script } from "./Script";
import { calculateRamUsage } from "./RamCalculations";
import { isScriptFilename } from "./ScriptHelpersTS";
@ -269,7 +271,7 @@ function saveAndCloseScriptEditor() {
}
//If the current script does NOT exist, create a new one
var script = new Script();
const script = new Script();
script.saveScript(getCurrentEditor().getCode(), Player);
s.scripts.push(script);
} else if (filename.endsWith(".txt")) {

@ -0,0 +1,45 @@
/**
* This is a component that implements a mathematical formula used commonly throughout the
* game. This formula is (typically) used to calculate the effect that various statistics
* have on a game mechanic. It looks something like:
*
* (stat ^ exponential factor) + (stat / linear factor)
*
* where the exponential factor is a number between 0 and 1 and the linear factor
* is typically a relatively larger number.
*
* This formula ensures that the effects of the statistic that is being processed
* has diminishing returns, but never loses its effectiveness as you continue
* to raise it.
*
* There are two implementations of this component. One is simply a function that
* can be called with the stat and the exponential/linear factors. The other is a
* class where the exponential and linear factors are defined upon construction.
*/
export function calculateEffectWithFactors(n: number, expFac: number, linearFac: number): number {
if (expFac <= 0 || expFac >= 1) {
console.warn(`Exponential factor is ${expFac}. This is not an intended value for it`);
}
if (linearFac < 1) {
console.warn(`Linear factor is ${linearFac}. This is not an intended value for it`);
}
return (Math.pow(n, expFac)) + (n / linearFac);
}
export class EffectWithFactors {
// Exponential factor
private expFac: number;
// Linear Factor
private linearFac: number;
constructor(expFac: number, linearFac: number) {
this.expFac = expFac;
this.linearFac = linearFac;
}
calculate(n: number): number {
return calculateEffectWithFactors(n, this.expFac, this.linearFac);
}
}

@ -6,6 +6,7 @@ interface IError {
}
export function exceptionAlert(e: IError): void {
console.error(e);
dialogBoxCreate("Caught an exception: " + e + "<br><br>" +
"Filename: " + (e.fileName || "UNKNOWN FILE NAME") + "<br><br>" +
"Line Number: " + (e.lineNumber || "UNKNOWN LINE NUMBER") + "<br><br>" +