From c3ecc189fdb86d36a0d604a45fcf3542c93fbbb8 Mon Sep 17 00:00:00 2001 From: danielyxie Date: Fri, 15 Mar 2019 02:37:06 -0700 Subject: [PATCH] Fixed numerous bugs with the recent Corporation UI rewrite. Rebalanced Corporation mechanic to give employees better and more interesting effects --- README.md | 9 +- css/companymanagement.scss | 3 +- src/BitNode/BitNode.ts | 3 + src/Bladeburner.js | 2 +- src/Constants.ts | 8 + src/Corporation/Corporation.jsx | 157 +++++++++--------- src/Corporation/Product.ts | 10 +- src/Corporation/ResearchTree.ts | 4 +- src/Corporation/Warehouse.ts | 29 +++- src/Corporation/ui/CityTabs.jsx | 6 +- .../ui/CorporationUIEventHandler.js | 102 +++++++----- src/Corporation/ui/IndustryOffice.jsx | 78 ++++++--- src/Corporation/ui/IndustryOverview.jsx | 31 +++- src/Corporation/ui/IndustryWarehouse.jsx | 103 +++++++----- src/Corporation/ui/MainPanel.jsx | 11 ++ src/Corporation/ui/Overview.jsx | 2 +- src/Script/ScriptHelpers.js | 4 +- src/utils/calculateEffectWithFactors.ts | 45 +++++ utils/helpers/exceptionAlert.ts | 1 + 19 files changed, 394 insertions(+), 214 deletions(-) create mode 100644 src/utils/calculateEffectWithFactors.ts diff --git a/README.md b/README.md index fa82712e8..1275437ab 100644 --- a/README.md +++ b/README.md @@ -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, @@ -32,4 +27,4 @@ publish, and distribute your contributions to the project. A formal Contributor's License Agreement will be drawn up in the future. If you would like to make significant contributions to the project as a -collaborator, please reach out to @danielyxie to help coordinate the effort. \ No newline at end of file +collaborator, please reach out to @danielyxie to help coordinate the effort. diff --git a/css/companymanagement.scss b/css/companymanagement.scss index bd632773f..7a8944a2b 100644 --- a/css/companymanagement.scss +++ b/css/companymanagement.scss @@ -159,5 +159,6 @@ /* Research */ #corporation-research-popup-box-content { - overflow-x: visible !important; + overflow-x: auto !important; + overflow-y: auto !important; } diff --git a/src/BitNode/BitNode.ts b/src/BitNode/BitNode.ts index dbafb8f85..7ac23b188 100644 --- a/src/BitNode/BitNode.ts +++ b/src/BitNode/BitNode.ts @@ -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; diff --git a/src/Bladeburner.js b/src/Bladeburner.js index 71357a0c6..dbf7677e3 100644 --- a/src/Bladeburner.js +++ b/src/Bladeburner.js @@ -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`); diff --git a/src/Constants.ts b/src/Constants.ts index cb34aeb8a..33022df26 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -291,7 +291,15 @@ export let CONSTANTS: IMap = { ** 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 ` } diff --git a/src/Corporation/Corporation.jsx b/src/Corporation/Corporation.jsx index e8d510de2..a91189136 100644 --- a/src/Corporation/Corporation.jsx +++ b/src/Corporation/Corporation.jsx @@ -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; diff --git a/src/Corporation/Product.ts b/src/Corporation/Product.ts index 6f8524e62..2a7d6afb1 100644 --- a/src/Corporation/Product.ts +++ b/src/Corporation/Product.ts @@ -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) + diff --git a/src/Corporation/ResearchTree.ts b/src/Corporation/ResearchTree.ts index 9cbdd8afa..8414631c8 100644 --- a/src/Corporation/ResearchTree.ts +++ b/src/Corporation/ResearchTree.ts @@ -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: `
` + - `${this.text}
${this.cost} Scientific Research` + + `${this.text}
${numeralWrapper.format(this.cost, "0,0")} Scientific Research` + `` + `${research.desc}` + `` + diff --git a/src/Corporation/Warehouse.ts b/src/Corporation/Warehouse.ts index 6f7a8cdd3..ecbaa826a 100644 --- a/src/Corporation/Warehouse.ts +++ b/src/Corporation/Warehouse.ts @@ -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") + "
"); + this.breakdown += (matName + ": " + numeralWrapper.format(mat.qty * MaterialSizes[matName], "0,0.0") + "
"); } } } @@ -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. diff --git a/src/Corporation/ui/CityTabs.jsx b/src/Corporation/ui/CityTabs.jsx index 0172a6802..56dfbf27b 100644 --- a/src/Corporation/ui/CityTabs.jsx +++ b/src/Corporation/ui/CityTabs.jsx @@ -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", diff --git a/src/Corporation/ui/CorporationUIEventHandler.js b/src/Corporation/ui/CorporationUIEventHandler.js index edbd70d56..6feec0808 100644 --- a/src/Corporation/ui/CorporationUIEventHandler.js +++ b/src/Corporation/ui/CorporationUIEventHandler.js @@ -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, }); diff --git a/src/Corporation/ui/IndustryOffice.jsx b/src/Corporation/ui/IndustryOffice.jsx index 566d44588..91d234a80 100644 --- a/src/Corporation/ui/IndustryOffice.jsx +++ b/src/Corporation/ui/IndustryOffice.jsx @@ -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 {

Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}

{ vechain && -
-

+

Material Production: {numeralWrapper.format(division.getOfficeProductivity(office), "0.000")} 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 -


-

+

+ } + { + vechain && +

Product Production: {numeralWrapper.format(division.getOfficeProductivity(office, {forProduct:true}), "0.000")} 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 -


-

- Business Multiplier: x" ${numeralWrapper.format(division.getBusinessFactor(office), "0.000")} +

+ } + { + vechain && +

+ Business Multiplier: x{numeralWrapper.format(division.getBusinessFactor(office), "0.000")} The effect this office's 'Business' employees has on boosting sales -


-
+

}

{EmployeePositions.Operations} ({this.state.numOperations}) - Manages supply chain operations. Improves production. + Manages supply chain operations. Improves the amount of Materials and Products you produce.

@@ -322,7 +348,9 @@ export class IndustryOffice extends BaseReactComponent {

{EmployeePositions.Engineer} ({this.state.numEngineers}) - 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)

@@ -332,7 +360,7 @@ export class IndustryOffice extends BaseReactComponent {

{EmployeePositions.Business} ({this.state.numBusiness}) - Handles sales and finances. Improves sales. + Handles sales and finances. Improves the amount of Materials and Products you can sell.

@@ -342,7 +370,8 @@ export class IndustryOffice extends BaseReactComponent {

{EmployeePositions.Management} ({this.state.numManagement}) - Leads and oversees employees and office operations. Improves production. + Leads and oversees employees and office operations. Improves the effectiveness of + Engineer and Operations employees

@@ -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(); + employeePositions.push(); + 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 {
+ { this.state.employee != null &&

@@ -462,15 +503,11 @@ export class IndustryOffice extends BaseReactComponent { } { this.state.employee != null && - {employeePositions} }

- -
) } @@ -495,7 +532,6 @@ export class IndustryOffice extends BaseReactComponent { } } - const hireEmployeeButtonOnClick = () => { office.findEmployees({ corporation: corp, industry: division }); } diff --git a/src/Corporation/ui/IndustryOverview.jsx b/src/Corporation/ui/IndustryOverview.jsx index f13e5106b..a108fba6d 100644 --- a/src/Corporation/ui/IndustryOverview.jsx +++ b/src/Corporation/ui/IndustryOverview.jsx @@ -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.

" + + "Below are approximations for how effective each material is at boosting " + + "this industry's production multiplier (Bigger bars = more effective):

" + + `Hardware:    ${convertEffectFacToGraphic(division.hwFac)}
` + + `Robots:      ${convertEffectFacToGraphic(division.robFac)}
` + + `AI Cores:    ${convertEffectFacToGraphic(division.aiFac)}
` + + `Real Estate: ${convertEffectFacToGraphic(division.reFac)}`); } return ( @@ -129,15 +146,15 @@ export class IndustryOverview extends BaseReactComponent { {popularity}
{ (advertisingInfo !== false) && -

Advertising Multiplier: {numeralWrapper.format(totalAdvertisingFac, "0.000")} +

Advertising Multiplier: x{numeralWrapper.format(totalAdvertisingFac, "0.000")} Total multiplier for this industrys sales due to its awareness and popularity
- Awareness Bonus: x{formatNumber(Math.pow(awarenessFac, 0.85), 3)} + Awareness Bonus: x{numeralWrapper.format(Math.pow(awarenessFac, 0.85), "0.000")}
- Popularity Bonus: x{formatNumber(Math.pow(popularityFac, 0.85), 3)} + Popularity Bonus: x{numeralWrapper.format(Math.pow(popularityFac, 0.85), "0.000")}
- Ratio Multiplier: x{formatNumber(Math.pow(ratioFac, 0.85), 3)} + Ratio Multiplier: x{numeralWrapper.format(Math.pow(ratioFac, 0.85), "0.000")}

} @@ -157,7 +174,7 @@ export class IndustryOverview extends BaseReactComponent {
?


- Scientific Research: {numeralWrapper.format(division.sciResearch.qty, "0.000")} + Scientific Research: {numeralWrapper.format(division.sciResearch.qty, "0.000a")} 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 } ) diff --git a/src/Corporation/ui/IndustryWarehouse.jsx b/src/Corporation/ui/IndustryWarehouse.jsx index 421ff5303..e13bb3cf7 100644 --- a/src/Corporation/ui/IndustryWarehouse.jsx +++ b/src/Corporation/ui/IndustryWarehouse.jsx @@ -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 ( -

-

Designing {product.name}...

+
+

Designing {product.name}...


{numeralWrapper.format(product.prog, "0.00")}% complete


@@ -76,8 +88,8 @@ function ProductComponent(props) { ) } else { return ( -
-

Designing {product.name}...

+
+

Designing {product.name}...


{numeralWrapper.format(product.prog, "0.00")}% complete

); @@ -87,13 +99,13 @@ function ProductComponent(props) { return (

- {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) - Prod: {numeralWrapper.format(product.data[city][1], nf)}/s + Prod: {numeralWrapper.format(product.data[city][1], nfB)}/s
- Sell: {numeralWrapper.format(product.data[city][2], nf)} /s + Sell: {numeralWrapper.format(product.data[city][2], nfB)} /s
-

+


Rating: {numeralWrapper.format(product.rat, nf)} @@ -119,13 +131,13 @@ function ProductComponent(props) { } -

+


Est. Production Cost: {numeralWrapper.formatMoney(product.pCost / ProductProductionCostRatio)} An estimate of the material cost it takes to create this Product. -

+


Est. Market Price: {numeralWrapper.formatMoney(product.pCost + product.rat / product.mku)} @@ -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) {

- {mat.name}: {numeralWrapper.format(mat.qty, nf)} ({numeralWrapper.format(totalGain, nf)}/s) + {mat.name}: {numeralWrapper.format(mat.qty, nfB)} ({numeralWrapper.format(totalGain, nfB)}/s) - Buy: {numeralWrapper.format(mat.buy, nf)}
- Prod: {numeralWrapper.format(mat.prd, nf)}
- Sell: {numeralWrapper.format(mat.sll, nf)}
- Export: {numeralWrapper.format(mat.totalExp, nf)}
- Import: {numeralWrapper.format(mat.imp, nf)} + Buy: {numeralWrapper.format(mat.buy, nfB)}
+ Prod: {numeralWrapper.format(mat.prd, nfB)}
+ Sell: {numeralWrapper.format(mat.sll, nfB)}
+ Export: {numeralWrapper.format(mat.totalExp, nfB)}
+ Import: {numeralWrapper.format(mat.imp, nfB)} { corp.unlockUpgrades[2] === 1 &&
} @@ -244,7 +261,7 @@ function MaterialComponent(props) {


- Quality: {numeralWrapper.format(mat.qlt, "0.00")} + Quality: {numeralWrapper.format(mat.qlt, "0.00a")} The quality of your material. Higher quality will lead to more sales @@ -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 {

Storage: {numeralWrapper.format(warehouse.sizeUsed, "0.000")} / {numeralWrapper.format(warehouse.size, "0.000")} - - {warehouse.breakdown} - +

} @@ -481,9 +498,11 @@ export class IndustryWarehouse extends BaseReactComponent { return this.renderWarehouseUI(); } else { return ( - +
+ +
) } } diff --git a/src/Corporation/ui/MainPanel.jsx b/src/Corporation/ui/MainPanel.jsx index f67827feb..03080e3b5 100644 --- a/src/Corporation/ui/MainPanel.jsx +++ b/src/Corporation/ui/MainPanel.jsx @@ -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 = ( ) diff --git a/src/Corporation/ui/Overview.jsx b/src/Corporation/ui/Overview.jsx index 6ae424863..aea620ec0 100644 --- a/src/Corporation/ui/Overview.jsx +++ b/src/Corporation/ui/Overview.jsx @@ -64,7 +64,7 @@ export class Overview extends BaseReactComponent { dividendStr + "Publicly Traded: " + (this.corp().public ? "Yes" : "No") + "
" + "Owned Stock Shares: " + numeralWrapper.format(this.corp().numShares, '0.000a') + "
" + - "Stock Price: " + (this.corp().public ? "$" + numeralWrapper.formatMoney(this.corp().sharePrice) : "N/A") + "
" + + "Stock Price: " + (this.corp().public ? numeralWrapper.formatMoney(this.corp().sharePrice) : "N/A") + "
" + "

Total Stock Shares: " + numeralWrapper.format(this.corp().totalShares, "0.000a") + "" + `Outstanding Shares: ${numeralWrapper.format(this.corp().issuedShares, "0.000a")}
` + diff --git a/src/Script/ScriptHelpers.js b/src/Script/ScriptHelpers.js index 821dce013..3eff77f47 100644 --- a/src/Script/ScriptHelpers.js +++ b/src/Script/ScriptHelpers.js @@ -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")) { diff --git a/src/utils/calculateEffectWithFactors.ts b/src/utils/calculateEffectWithFactors.ts new file mode 100644 index 000000000..fc394ff3c --- /dev/null +++ b/src/utils/calculateEffectWithFactors.ts @@ -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); + } +} diff --git a/utils/helpers/exceptionAlert.ts b/utils/helpers/exceptionAlert.ts index c5bc571e3..2a6998c89 100644 --- a/utils/helpers/exceptionAlert.ts +++ b/utils/helpers/exceptionAlert.ts @@ -6,6 +6,7 @@ interface IError { } export function exceptionAlert(e: IError): void { + console.error(e); dialogBoxCreate("Caught an exception: " + e + "

" + "Filename: " + (e.fileName || "UNKNOWN FILE NAME") + "

" + "Line Number: " + (e.lineNumber || "UNKNOWN LINE NUMBER") + "

" +