diff --git a/src/Corporation/Corporation.jsx b/src/Corporation/Corporation.jsx index 764aac225..dbeb09ed5 100644 --- a/src/Corporation/Corporation.jsx +++ b/src/Corporation/Corporation.jsx @@ -47,7 +47,6 @@ import { yesNoBoxCreate, // UI Related Imports import React from "react"; import ReactDOM from "react-dom"; -import { CorporationEventHandler } from "./ui/CorporationUIEventHandler"; import { CorporationRoot } from "./ui/Root"; import { CorporationRouting } from "./ui/Routing"; @@ -1884,7 +1883,6 @@ Corporation.prototype.getStarterGuide = function() { } let corpRouting; -let eventHandler; let companyManagementDiv; Corporation.prototype.createUI = function() { companyManagementDiv = createElement("div", { @@ -1895,13 +1893,12 @@ Corporation.prototype.createUI = function() { document.getElementById("entire-game-container").appendChild(companyManagementDiv); corpRouting = new CorporationRouting(this); - eventHandler = new CorporationEventHandler(this, corpRouting); this.rerender(); } Corporation.prototype.rerender = function() { - if (companyManagementDiv == null || corpRouting == null || eventHandler == null) { + if (companyManagementDiv == null || corpRouting == null) { console.warn(`Corporation.rerender() called when companyManagementDiv, corpRouting, or eventHandler is null`); return; } @@ -1910,7 +1907,6 @@ Corporation.prototype.rerender = function() { ReactDOM.render(, companyManagementDiv); } diff --git a/src/Corporation/ui/CityTabs.tsx b/src/Corporation/ui/CityTabs.tsx index 6f4e1205b..6e2255d8a 100644 --- a/src/Corporation/ui/CityTabs.tsx +++ b/src/Corporation/ui/CityTabs.tsx @@ -7,7 +7,6 @@ import { createPopup } from "../../ui/React/createPopup"; import { IDivision } from "../IDivision"; interface IProps { - eventHandler: any; routing: any; onClicks: {[key: string]: () => void}; city: string; // currentCity diff --git a/src/Corporation/ui/CorporationUIEventHandler.js b/src/Corporation/ui/CorporationUIEventHandler.js deleted file mode 100644 index 3f586d8cf..000000000 --- a/src/Corporation/ui/CorporationUIEventHandler.js +++ /dev/null @@ -1,578 +0,0 @@ -// Creates a class for handling UI events, such as clicks and keyboard events -import { CorporationRouting } from "./Routing"; -import { Corporation, - Industry, - Warehouse, - DividendMaxPercentage, - IssueNewSharesCooldown } from "../Corporation"; -import { OfficeSpace } from "../OfficeSpace"; - -import { Industries, - IndustryStartingCosts, - IndustryDescriptions, -} from "../IndustryData"; - -import { MaterialSizes } from "../MaterialSizes"; - -import { Product } from "../Product"; - -import { Cities } from "../../Locations/Cities"; - -import { numeralWrapper } from "../../ui/numeralFormat"; - -import { dialogBoxCreate } from "../../../utils/DialogBox"; - -import { getRandomInt } from "../../../utils/helpers/getRandomInt"; -import { KEY } from "../../../utils/helpers/keyCodes"; - -import { clearSelector } from "../../../utils/uiHelpers/clearSelector"; -import { createElement } from "../../../utils/uiHelpers/createElement"; -import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement"; -import { createPopup } from "../../../utils/uiHelpers/createPopup"; -import { createPopupCloseButton } from "../../../utils/uiHelpers/createPopupCloseButton"; -import { getSelectText, - getSelectValue } from "../../../utils/uiHelpers/getSelectData"; -import { removeElementById } from "../../../utils/uiHelpers/removeElementById"; - -export class CorporationEventHandler { - constructor(corp, routing) { - if (!(corp instanceof Corporation)) { - throw new Error(`CorporationEventHandler constructed without proper Corporation instance`); - } - if (!(routing instanceof CorporationRouting)) { - throw new Error(`CorporationEventHandler constructed without proper CorporationRouting instance`); - } - - this.corp = corp; - this.routing = routing; - } - - // Create a popup that lets the player use the Market TA research for Products - createProductMarketTaPopup(product, industry) { - const popupId = "cmpy-mgmt-marketta-popup"; - const markupLimit = product.rat / product.mku; - const ta1 = createElement("p", { - innerHTML: "Market-TA.I
" + - "The maximum sale price you can mark this up to is " + - numeralWrapper.formatMoney(product.pCost + markupLimit) + - ". This means that if you set the sale price higher than this, " + - "you will begin to experience a loss in number of sales", - }); - - // Enable using Market-TA1 for automatically setting sale price - const useTa1AutoSaleId = "cmpy-mgmt-marketa1-checkbox"; - const useTa1AutoSaleDiv = createElement("div", { display: "block" }); - const useTa1AutoSaleLabel = createElement("label", { - color: "white", - for: useTa1AutoSaleId, - innerText: "Use Market-TA.I for Auto-Sale Price", - tooltip: "If this is enabled, then this Product will automatically " + - "be sold at the price identified by Market-TA.I (i.e. the price shown above)", - }) - const useTa1AutoSaleCheckbox = createElement("input", { - checked: product.marketTa1, - id: useTa1AutoSaleId, - margin: "3px", - type: "checkbox", - changeListener: (e) => { - product.marketTa1 = e.target.checked; - }, - }); - useTa1AutoSaleDiv.appendChild(useTa1AutoSaleLabel); - useTa1AutoSaleDiv.appendChild(useTa1AutoSaleCheckbox); - - const closeBtn = createPopupCloseButton(popupId, { - class: "std-button", - display: "block", - innerText: "Close", - }); - - if (industry.hasResearch("Market-TA.II")) { - let updateTa2Text; - const ta2Text = createElement("p"); - const ta2Input = createElement("input", { - marginTop: "4px", - onkeyup: (e) => { - e.preventDefault(); - updateTa2Text(); - }, - type: "number", - value: product.pCost, - }); - - // Function that updates the text in ta2Text element - updateTa2Text = function() { - const sCost = parseFloat(ta2Input.value); - let markup = 1; - if (sCost > product.pCost) { - if ((sCost - product.pCost) > markupLimit) { - markup = markupLimit / (sCost - product.pCost); - } - } - ta2Text.innerHTML = `
Market-TA.II
` + - `If you sell at ${numeralWrapper.formatMoney(sCost)}, ` + - `then you will sell ${numeralWrapper.format(markup, "0.00000")}x as much compared ` + - `to if you sold at market price.`; - } - updateTa2Text(); - - // Enable using Market-TA2 for automatically setting sale price - const useTa2AutoSaleId = "cmpy-mgmt-marketa2-checkbox"; - const useTa2AutoSaleDiv = createElement("div", { display: "block" }); - const useTa2AutoSaleLabel = createElement("label", { - color: "white", - for: useTa2AutoSaleId, - innerText: "Use Market-TA.II for Auto-Sale Price", - tooltip: "If this is enabled, then this Product will automatically " + - "be sold at the optimal price such that the amount sold matches the " + - "amount produced. (i.e. the highest possible price, while still ensuring " + - " that all produced materials will be sold)", - }) - const useTa2AutoSaleCheckbox = createElement("input", { - checked: product.marketTa2, - id: useTa2AutoSaleId, - margin: "3px", - type: "checkbox", - changeListener: (e) => { - product.marketTa2 = e.target.checked; - }, - }); - useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel); - useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox); - - const ta2OverridesTa1 = createElement("p", { - innerText: "Note that Market-TA.II overrides Market-TA.I. This means that if " + - "both are enabled, then Market-TA.II will take effect, not Market-TA.I", - }); - - createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, ta2OverridesTa1, closeBtn]); - } else { - // Market-TA.I only - createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]); - } - } - - // Create a popup that lets the player purchase a Material - createPurchaseMaterialPopup(mat, industry, warehouse) { - const corp = this.corp; - - const purchasePopupId = "cmpy-mgmt-material-purchase-popup"; - const txt = createElement("p", { - innerHTML: "Enter the amount of " + mat.name + " you would like " + - "to purchase per second. This material's cost changes constantly", - }); - let confirmBtn; - let input = createElement("input", { - margin: "5px", - placeholder: "Purchase amount", - type: "number", - value: mat.buy ? mat.buy : null, - onkeyup: (e) => { - e.preventDefault(); - if (e.keyCode === KEY.ENTER) {confirmBtn.click();} - }, - }); - confirmBtn = createElement("button", { - innerText: "Confirm", class: "std-button", - clickListener: () => { - if (isNaN(input.value)) { - dialogBoxCreate("Invalid amount"); - } else { - mat.buy = parseFloat(input.value); - if (isNaN(mat.buy)) {mat.buy = 0;} - removeElementById(purchasePopupId); - this.rerender(); - return false; - } - }, - }); - const clearButton = createElement("button", { - innerText: "Clear Purchase", class: "std-button", - clickListener: () => { - mat.buy = 0; - removeElementById(purchasePopupId); - this.rerender(); - return false; - }, - }); - const cancelBtn = createPopupCloseButton(purchasePopupId, { - class: "std-button", - innerText: "Cancel", - }); - - const elems = [txt, input, confirmBtn, clearButton, cancelBtn]; - - if (industry.hasResearch("Bulk Purchasing")) { - const bulkPurchaseInfo = createElement("p", { - innerText: "Enter the amount of " + mat.name + " you would like " + - "to bulk purchase. This purchases the specified amount instantly " + - "(all at once).", - }); - - let bulkPurchaseCostTxt = createElement("p"); - function updateBulkPurchaseText(amount) { - const parsedAmt = parseFloat(amount); - const cost = parsedAmt * mat.bCost; - - const matSize = MaterialSizes[mat.name]; - const maxAmount = ((warehouse.size - warehouse.sizeUsed) / matSize); - - if (parsedAmt * matSize > maxAmount) { - bulkPurchaseCostTxt.innerText = "Not enough warehouse space to purchase this amount"; - } else if (isNaN(cost)) { - bulkPurchaseCostTxt.innerText = "Invalid put for Bulk Purchase amount"; - } else { - bulkPurchaseCostTxt.innerText = `Purchasing ${numeralWrapper.format(parsedAmt, "0,0.00")} of ` + - `${mat.name} will cost ${numeralWrapper.formatMoney(cost)}`; - } - } - - let bulkPurchaseConfirmBtn; - const bulkPurchaseInput = createElement("input", { - margin: "5px", - placeholder: "Bulk Purchase amount", - type: "number", - onkeyup: (e) => { - e.preventDefault(); - updateBulkPurchaseText(e.target.value); - if (e.keyCode === KEY.ENTER) {bulkPurchaseConfirmBtn.click();} - }, - }); - - bulkPurchaseConfirmBtn = createElement("button", { - class: "std-button", - innerText: "Confirm Bulk Purchase", - clickListener: () => { - const amount = parseFloat(bulkPurchaseInput.value); - - const matSize = MaterialSizes[mat.name]; - const maxAmount = ((warehouse.size - warehouse.sizeUsed) / matSize); - if (amount * matSize > maxAmount) { - dialogBoxCreate(`You do not have enough warehouse size to fit this purchase`); - return false; - } - - if (isNaN(amount)) { - dialogBoxCreate("Invalid input amount"); - } else { - const cost = amount * mat.bCost; - if (corp.funds.gt(cost)) { - corp.funds = corp.funds.minus(cost); - mat.qty += amount; - } else { - dialogBoxCreate(`You cannot afford this purchase.`); - return false; - } - - removeElementById(purchasePopupId); - return false; - } - }, - }) - - elems.push(bulkPurchaseInfo); - elems.push(bulkPurchaseCostTxt); - elems.push(bulkPurchaseInput); - elems.push(bulkPurchaseConfirmBtn); - } - - createPopup(purchasePopupId, elems); - input.focus(); - } - - // Create a popup that let the player manage sales of a material - createSellMaterialPopup(mat) { - const sellPopupId = "cmpy-mgmt-material-sell-popup"; - const txt = createElement("p", { - innerHTML: "Enter the maximum amount of " + mat.name + " you would like " + - "to sell per second, as well as the price at which you would " + - "like to sell at.

" + - "If the sell amount is set to 0, then the material will not be sold. If the sell price " + - "if set to 0, then the material will be discarded

" + - "Setting the sell amount to 'MAX' will result in you always selling the " + - "maximum possible amount of the material.

" + - "When setting the sell amount, you can use the 'PROD' variable to designate a dynamically " + - "changing amount that depends on your production. For example, if you set the sell amount " + - "to 'PROD-5' then you will always sell 5 less of the material than you produce.

" + - "When setting the sell price, you can use the 'MP' variable to designate a dynamically " + - "changing price that depends on the market price. For example, if you set the sell price " + - "to 'MP+10' then it will always be sold at $10 above the market price.", - }); - const br = createElement("br"); - let confirmBtn; - const inputQty = createElement("input", { - type: "text", marginTop: "4px", - value: mat.sllman[1] ? mat.sllman[1] : null, - placeholder: "Sell amount", - onkeyup: (e) => { - e.preventDefault(); - if (e.keyCode === KEY.ENTER) {confirmBtn.click();} - }, - }); - - let inputButtonInitValue = mat.sCost ? mat.sCost : null; - if (mat.marketTa2) { - inputButtonInitValue += " (Market-TA.II)"; - } else if (mat.marketTa1) { - inputButtonInitValue += " (Market-TA.I)"; - } - - const inputPx = createElement("input", { - type: "text", marginTop: "4px", - value: inputButtonInitValue, - placeholder: "Sell price", - onkeyup: (e) => { - e.preventDefault(); - if (e.keyCode === KEY.ENTER) {confirmBtn.click();} - }, - }); - confirmBtn = createElement("button", { - class: "std-button", - innerText: "Confirm", - clickListener: () => { - //Parse price - let cost = inputPx.value.replace(/\s+/g, ''); - cost = cost.replace(/[^-()\d/*+.MP]/g, ''); //Sanitize cost - let temp = cost.replace(/MP/g, mat.bCost); - try { - temp = eval(temp); - } catch(e) { - dialogBoxCreate("Invalid value or expression for sell price field: " + e); - return false; - } - - if (temp == null || isNaN(temp)) { - dialogBoxCreate("Invalid value or expression for sell price field"); - return false; - } - - if (cost.includes("MP")) { - mat.sCost = cost; //Dynamically evaluated - } else { - mat.sCost = temp; - } - - //Parse quantity - if (inputQty.value.includes("MAX") || inputQty.value.includes("PROD")) { - let qty = inputQty.value.replace(/\s+/g, ''); - qty = qty.replace(/[^-()\d/*+.MAXPROD]/g, ''); - let tempQty = qty.replace(/MAX/g, 1); - tempQty = tempQty.replace(/PROD/g, 1); - try { - tempQty = eval(tempQty); - } catch(e) { - dialogBoxCreate("Invalid value or expression for sell price field: " + e); - return false; - } - - if (tempQty == null || isNaN(tempQty)) { - dialogBoxCreate("Invalid value or expression for sell price field"); - return false; - } - - mat.sllman[0] = true; - mat.sllman[1] = qty; //Use sanitized input - } else if (isNaN(inputQty.value)) { - dialogBoxCreate("Invalid value for sell quantity field! Must be numeric or 'MAX'"); - return false; - } else { - var qty = parseFloat(inputQty.value); - if (isNaN(qty)) {qty = 0;} - if (qty === 0) { - mat.sllman[0] = false; - mat.sllman[1] = 0; - } else { - mat.sllman[0] = true; - mat.sllman[1] = qty; - } - } - - removeElementById(sellPopupId); - this.rerender(); - return false; - }, - }); - const cancelBtn = createPopupCloseButton(sellPopupId, { - class: "std-button", - innerText: "Cancel", - }); - - createPopup(sellPopupId, [txt, br, inputQty, inputPx, confirmBtn, cancelBtn]); - inputQty.focus(); - } - - // Create a popup that lets the player manage sales of the 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 " + - "to sell per second, as well as the price at which you would like to " + - "sell it at.

" + - "If the sell amount is set to 0, then the product will not be sold. If the " + - "sell price is set to 0, then the product will be discarded.

" + - "Setting the sell amount to 'MAX' will result in you always selling the " + - "maximum possible amount of the material.

" + - "When setting the sell amount, you can use the 'PROD' variable to designate a " + - "dynamically changing amount that depends on your production. For example, " + - "if you set the sell amount to 'PROD-1' then you will always sell 1 less of " + - "the material than you produce.

" + - "When setting the sell price, you can use the 'MP' variable to set a " + - "dynamically changing price that depends on the Product's estimated " + - "market price. For example, if you set it to 'MP*5' then it " + - "will always be sold at five times the estimated market price.", - }); - 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, - onkeyup: (e) => { - e.preventDefault(); - if (e.keyCode === KEY.ENTER) {confirmBtn.click();} - }, - }); - - let inputButtonInitValue = product.sCost ? product.sCost : null; - if (product.marketTa2) { - inputButtonInitValue += " (Market-TA.II)"; - } else if (product.marketTa1) { - inputButtonInitValue += " (Market-TA.I)"; - } - - const inputPx = createElement("input", { - margin: "5px 0px 5px 0px", - placeholder: "Sell price", - type: "text", - value: inputButtonInitValue, - onkeyup: (e) => { - e.preventDefault(); - if (e.keyCode === KEY.ENTER) {confirmBtn.click();} - }, - }); - const checkboxDiv = createElement("div", { - border: "1px solid white", - display: "inline-block", - }) - const checkboxLabel = createElement("label", { - for: popupId + "-checkbox", - innerText: "Use same 'Sell Amount' for all cities", - }); - const checkbox = createElement("input", { - checked: true, - id: popupId + "-checkbox", - margin: "2px", - type: "checkbox", - }); - checkboxDiv.appendChild(checkboxLabel); - checkboxDiv.appendChild(checkbox); - - confirmBtn = createElement("button", { - class: "std-button", - innerText: "Confirm", - clickListener: () => { - //Parse price - if (inputPx.value.includes("MP")) { - //Dynamically evaluated quantity. First test to make sure its valid - //Sanitize input, then replace dynamic variables with arbitrary numbers - var price = inputPx.value.replace(/\s+/g, ''); - price = price.replace(/[^-()\d/*+.MP]/g, ''); - var temp = price.replace(/MP/g, 1); - try { - temp = eval(temp); - } catch(e) { - dialogBoxCreate("Invalid value or expression for sell quantity field: " + e); - return false; - } - if (temp == null || isNaN(temp)) { - dialogBoxCreate("Invalid value or expression for sell quantity field."); - return false; - } - product.sCost = price; //Use sanitized price - } else { - var cost = parseFloat(inputPx.value); - if (isNaN(cost)) { - dialogBoxCreate("Invalid value for sell price field"); - return false; - } - product.sCost = cost; - } - - // Array of all cities. Used later - const cities = Object.keys(Cities); - - // Parse quantity - if (inputQty.value.includes("MAX") || inputQty.value.includes("PROD")) { - //Dynamically evaluated quantity. First test to make sure its valid - var qty = inputQty.value.replace(/\s+/g, ''); - qty = qty.replace(/[^-()\d/*+.MAXPROD]/g, ''); - var temp = qty.replace(/MAX/g, 1); - temp = temp.replace(/PROD/g, 1); - try { - temp = eval(temp); - } catch(e) { - dialogBoxCreate("Invalid value or expression for sell price field: " + e); - return false; - } - - if (temp == null || isNaN(temp)) { - dialogBoxCreate("Invalid value or expression for sell price field"); - return false; - } - if (checkbox.checked) { - for (let i = 0; i < cities.length; ++i) { - const tempCity = cities[i]; - product.sllman[tempCity][0] = true; - product.sllman[tempCity][1] = qty; //Use sanitized input - } - } else { - product.sllman[city][0] = true; - product.sllman[city][1] = qty; //Use sanitized input - } - } else if (isNaN(inputQty.value)) { - dialogBoxCreate("Invalid value for sell quantity field! Must be numeric"); - return false; - } else { - var qty = parseFloat(inputQty.value); - if (isNaN(qty)) {qty = 0;} - if (qty === 0) { - if (checkbox.checked) { - for (let i = 0; i < cities.length; ++i) { - const tempCity = cities[i]; - product.sllman[tempCity][0] = false; - } - } else { - product.sllman[city][0] = false; - } - } else { - if (checkbox.checked) { - for (let i = 0; i < cities.length; ++i) { - const tempCity = cities[i]; - product.sllman[tempCity][0] = true; - product.sllman[tempCity][1] = qty; - } - } else { - product.sllman[city][0] = true; - product.sllman[city][1] = qty; - } - } - } - - removeElementById(popupId); - this.rerender(); - return false; - }, - }); - const cancelBtn = createPopupCloseButton(popupId, { class: "std-button" }); - - const linebreak1 = createElement("br"); - - createPopup(popupId, [txt, inputQty, inputPx, confirmBtn, cancelBtn, linebreak1, - checkboxDiv]); - inputQty.focus(); - } - - rerender() { - this.corp.rerender(); - } -} diff --git a/src/Corporation/ui/HeaderTabs.tsx b/src/Corporation/ui/HeaderTabs.tsx index b22bc0277..28d61c6c7 100644 --- a/src/Corporation/ui/HeaderTabs.tsx +++ b/src/Corporation/ui/HeaderTabs.tsx @@ -9,7 +9,6 @@ import { createPopup } from "../../ui/React/createPopup"; interface IProps { corp: any; - eventHandler: any; routing: any; } diff --git a/src/Corporation/ui/Industry.tsx b/src/Corporation/ui/Industry.tsx index ef2dac622..e68fcd2d4 100644 --- a/src/Corporation/ui/Industry.tsx +++ b/src/Corporation/ui/Industry.tsx @@ -8,7 +8,6 @@ import { IndustryWarehouse } from "./IndustryWarehouse"; interface IProps { routing: any; - eventHandler: any; corp: any; currentCity: string; } @@ -19,12 +18,10 @@ export function Industry(props: IProps): React.ReactElement {
@@ -32,8 +29,7 @@ export function Industry(props: IProps): React.ReactElement { + currentCity={props.currentCity} /> ) diff --git a/src/Corporation/ui/IndustryOffice.tsx b/src/Corporation/ui/IndustryOffice.tsx index a71b12096..a88fb883e 100644 --- a/src/Corporation/ui/IndustryOffice.tsx +++ b/src/Corporation/ui/IndustryOffice.tsx @@ -15,7 +15,6 @@ import { ThrowPartyPopup } from "./ThrowPartyPopup"; interface IProps { routing: any; - eventHandler: any; corp: any; currentCity: string; } diff --git a/src/Corporation/ui/IndustryOverview.tsx b/src/Corporation/ui/IndustryOverview.tsx index 06d4d2f44..258cbb879 100644 --- a/src/Corporation/ui/IndustryOverview.tsx +++ b/src/Corporation/ui/IndustryOverview.tsx @@ -13,7 +13,6 @@ import { createPopup } from "../../ui/React/createPopup"; interface IProps { routing: any; - eventHandler: any; corp: any; currentCity: string; } diff --git a/src/Corporation/ui/IndustryWarehouse.tsx b/src/Corporation/ui/IndustryWarehouse.tsx index 9ac826feb..a44259540 100644 --- a/src/Corporation/ui/IndustryWarehouse.tsx +++ b/src/Corporation/ui/IndustryWarehouse.tsx @@ -11,6 +11,10 @@ import { DiscontinueProductPopup } from "./DiscontinueProductPopup"; import { ExportPopup } from "./ExportPopup"; import { LimitProductProductionPopup } from "./LimitProductProductionPopup"; import { MaterialMarketTaPopup } from "./MaterialMarketTaPopup"; +import { SellMaterialPopup } from "./SellMaterialPopup"; +import { SellProductPopup } from "./SellProductPopup"; +import { PurchaseMaterialPopup } from "./PurchaseMaterialPopup"; +import { ProductMarketTaPopup } from "./ProductMarketTaPopup"; import { numeralWrapper } from "../../ui/numeralFormat"; import { dialogBoxCreate } from "../../../utils/DialogBox"; @@ -23,7 +27,6 @@ interface IProductProps { division: any; city: string; product: any; - eventHandler: any; } // Creates the UI for a single Product type @@ -32,7 +35,6 @@ function ProductComponent(props: IProductProps): React.ReactElement { const division = props.division; const city = props.city; const product = props.product; - const eventHandler = props.eventHandler; // Numeraljs formatters const nf = "0.000"; @@ -67,7 +69,15 @@ function ProductComponent(props: IProductProps): React.ReactElement { sellButtonText += (" @ " + numeralWrapper.format(product.sCost, "$0.000a")); } } - const sellButtonOnClick = eventHandler.createSellProductPopup.bind(eventHandler, product, city); + + function openSellProductPopup(): void { + const popupId = "cmpy-mgmt-limit-product-production-popup"; + createPopup(popupId, SellProductPopup, { + product: product, + city: city, + popupId: popupId, + }); + } // Limit Production button let limitProductionButtonText = "Limit Production"; @@ -94,8 +104,14 @@ function ProductComponent(props: IProductProps): React.ReactElement { }); } - // Market TA button - const marketTaButtonOnClick = eventHandler.createProductMarketTaPopup.bind(eventHandler, product, division); + function openProductMarketTaPopup(): void { + const popupId = "cmpy-mgmt-marketta-popup"; + createPopup(popupId, ProductMarketTaPopup, { + product: product, + industry: division, + popupId: popupId, + }); + } // Unfinished Product if (!product.fin) { @@ -107,7 +123,7 @@ function ProductComponent(props: IProductProps): React.ReactElement {
-
{ division.hasResearch("Market-TA.I") && - } @@ -187,7 +203,7 @@ function ProductComponent(props: IProductProps): React.ReactElement {

-
{ division.hasResearch("Market-TA.I") && - } @@ -213,7 +229,6 @@ interface IMaterialProps { warehouse: any; city: string; mat: any; - eventHandler: any; } // Creates the UI for a single Material type @@ -223,7 +238,6 @@ function MaterialComponent(props: IMaterialProps): React.ReactElement { const warehouse = props.warehouse; const city = props.city; const mat = props.mat; - const eventHandler = props.eventHandler; const markupLimit = mat.getMarkupLimit(); const office = division.offices[city]; if (!(office instanceof OfficeSpace)) { @@ -245,7 +259,17 @@ function MaterialComponent(props: IMaterialProps): React.ReactElement { // Purchase material button const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nfB)})`; const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button"; - const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division, warehouse); + + function openPurchaseMaterialPopup() { + const popupId = "cmpy-mgmt-material-purchase-popup"; + createPopup(popupId, PurchaseMaterialPopup, { + mat: mat, + industry: division, + warehouse: warehouse, + corp: props.corp, + popupId: popupId, + }); + } function openExportPopup() { const popupId = "cmpy-mgmt-export-popup"; @@ -280,7 +304,15 @@ function MaterialComponent(props: IMaterialProps): React.ReactElement { } else { sellButtonText = "Sell (0.000/0.000)"; } - const sellButtonOnClick = eventHandler.createSellMaterialPopup.bind(eventHandler, mat); + + function openSellMaterialPopup(): void { + const popupId = "cmpy-mgmt-material-sell-popup"; + createPopup(popupId, SellMaterialPopup, { + mat: mat, + corp: props.corp, + popupId: popupId, + }); + } function openMaterialMarketTaPopup(): void { const popupId = "cmpy-mgmt-export-popup"; @@ -334,7 +366,7 @@ function MaterialComponent(props: IMaterialProps): React.ReactElement {
- @@ -372,7 +404,6 @@ interface IProps { corp: any; routing: any; currentCity: string; - eventHandler: any; } export function IndustryWarehouse(props: IProps): React.ReactElement { @@ -485,7 +516,6 @@ export function IndustryWarehouse(props: IProps): React.ReactElement { city={props.currentCity} corp={corp} division={division} - eventHandler={props.eventHandler} key={matName} mat={warehouse.materials[matName]} warehouse={warehouse} />); @@ -502,7 +532,6 @@ export function IndustryWarehouse(props: IProps): React.ReactElement { city={props.currentCity} corp={corp} division={division} - eventHandler={props.eventHandler} key={productName} product={division.products[productName]} />); diff --git a/src/Corporation/ui/MainPanel.tsx b/src/Corporation/ui/MainPanel.tsx index e01d846d1..c1ca735da 100644 --- a/src/Corporation/ui/MainPanel.tsx +++ b/src/Corporation/ui/MainPanel.tsx @@ -14,7 +14,6 @@ import { IPlayer } from "../../PersonObjects/IPlayer"; interface IProps { corp: any; - eventHandler: any; routing: any; player: IPlayer; } diff --git a/src/Corporation/ui/Overview.tsx b/src/Corporation/ui/Overview.tsx index 2d035d448..c9ad865d8 100644 --- a/src/Corporation/ui/Overview.tsx +++ b/src/Corporation/ui/Overview.tsx @@ -20,7 +20,6 @@ import { IPlayer } from "../../PersonObjects/IPlayer"; interface IProps { corp: any; - eventHandler: any; player: IPlayer; } diff --git a/src/Corporation/ui/ProductMarketTaPopup.tsx b/src/Corporation/ui/ProductMarketTaPopup.tsx new file mode 100644 index 000000000..04b254e84 --- /dev/null +++ b/src/Corporation/ui/ProductMarketTaPopup.tsx @@ -0,0 +1,105 @@ +import React, { useState } from 'react'; +import { Warehouse } from "../Warehouse"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; +import { createElement } from "../../../utils/uiHelpers/createElement"; +import { removePopup } from "../../ui/React/createPopup"; +import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement"; +import { clearSelector } from "../../../utils/uiHelpers/clearSelector"; +import { getSelectText, + getSelectValue } from "../../../utils/uiHelpers/getSelectData"; +import { numeralWrapper } from "../../ui/numeralFormat"; + +interface IProps { + product: any; + industry: any; + popupId: string; +} + +function MarketTA2(props: IProps): React.ReactElement { + const markupLimit = props.product.rat / props.product.mku; + const [value, setValue] = useState(props.product.pCost); + const setRerender = useState(false)[1]; + function rerender(): void { + setRerender(old => !old); + } + + function onChange(event: React.ChangeEvent): void { + setValue(event.target.value); + } + + + function onCheckedChange(event: React.ChangeEvent): void { + props.product.marketTa2 = event.target.checked; + rerender(); + } + + const sCost = parseFloat(value); + let markup = 1; + if (sCost > props.product.pCost) { + if ((sCost - props.product.pCost) > markupLimit) { + markup = markupLimit / (sCost - props.product.pCost); + } + } + + return (<> +

+
Market-TA.II
+ If you sell at {numeralWrapper.formatMoney(sCost)}, + then you will sell {numeralWrapper.format(markup, "0.00000")}x as much compared + to if you sold at market price. +

+ +
+ + +
+

+ Note that Market-TA.II overrides Market-TA.I. This means that if + both are enabled, then Market-TA.II will take effect, not Market-TA.I +

+ ); +} + +// Create a popup that lets the player use the Market TA research for Products +export function ProductMarketTaPopup(props: IProps): React.ReactElement { + const markupLimit = props.product.rat / props.product.mku; + const setRerender = useState(false)[1]; + function rerender(): void { + setRerender(old => !old); + } + + function onChange(event: React.ChangeEvent): void { + props.product.marketTa1 = event.target.checked; + rerender(); + } + + return (<> +

+ Market-TA.I
+ The maximum sale price you can mark this up to + is {numeralWrapper.formatMoney(props.product.pCost + markupLimit)}. + This means that if you set the sale price higher than this, + you will begin to experience a loss in number of sales. +

+
+ + +
+ {props.industry.hasResearch("Market-TA.II") && } + ); +} diff --git a/src/Corporation/ui/PurchaseMaterialPopup.tsx b/src/Corporation/ui/PurchaseMaterialPopup.tsx new file mode 100644 index 000000000..891de894c --- /dev/null +++ b/src/Corporation/ui/PurchaseMaterialPopup.tsx @@ -0,0 +1,130 @@ +import React, { useState } from 'react'; +import { Warehouse } from "../Warehouse"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; +import { createElement } from "../../../utils/uiHelpers/createElement"; +import { removePopup } from "../../ui/React/createPopup"; +import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement"; +import { clearSelector } from "../../../utils/uiHelpers/clearSelector"; +import { getSelectText, + getSelectValue } from "../../../utils/uiHelpers/getSelectData"; +import { MaterialSizes } from "../MaterialSizes"; +import { numeralWrapper } from "../../ui/numeralFormat"; + +interface IBulkPurchaseTextProps { + warehouse: any; + mat: any; + amount: string; +} + +function BulkPurchaseText(props: IBulkPurchaseTextProps): React.ReactElement { + const parsedAmt = parseFloat(props.amount); + const cost = parsedAmt * props.mat.bCost; + + const matSize = MaterialSizes[props.mat.name]; + const maxAmount = ((props.warehouse.size - props.warehouse.sizeUsed) / matSize); + + if (parsedAmt * matSize > maxAmount) { + return (<>Not enough warehouse space to purchase this amount); + } else if (isNaN(cost)) { + return (<>Invalid put for Bulk Purchase amount); + } else { + return (<>Purchasing {numeralWrapper.format(parsedAmt, "0,0.00")} of + {props.mat.name} will cost {numeralWrapper.formatMoney(cost)}); + } +} + +interface IProps { + mat: any; + industry: any; + warehouse: any; + corp: any; + popupId: string; +} + +function BulkPurchase(props: IProps): React.ReactElement { + const [buyAmt, setBuyAmt] = useState(''); + + function bulkPurchase(): void { + const amount = parseFloat(buyAmt); + + const matSize = MaterialSizes[props.mat.name]; + const maxAmount = ((props.warehouse.size - props.warehouse.sizeUsed) / matSize); + if (amount * matSize > maxAmount) { + dialogBoxCreate(`You do not have enough warehouse size to fit this purchase`); + return; + } + + if (isNaN(amount)) { + dialogBoxCreate("Invalid input amount"); + } else { + const cost = amount * props.mat.bCost; + if (props.corp.funds.gt(cost)) { + props.corp.funds = props.corp.funds.minus(cost); + props.mat.qty += amount; + } else { + dialogBoxCreate(`You cannot afford this purchase.`); + return; + } + + removePopup(props.popupId); + } + } + + function onKeyDown(event: React.KeyboardEvent): void { + if (event.keyCode === 13) bulkPurchase(); + } + + function onChange(event: React.ChangeEvent): void { + setBuyAmt(event.target.value); + } + + return (<> +

+ Enter the amount of {props.mat.name} you would like + to bulk purchase. This purchases the specified amount instantly + (all at once). +

+ + + + ); +} + +// Create a popup that lets the player purchase a Material +export function PurchaseMaterialPopup(props: IProps): React.ReactElement { + const [buyAmt, setBuyAmt] = useState(props.mat.buy ? props.mat.buy : null); + + function purchaseMaterial(): void { + if (isNaN(parseFloat(buyAmt))) { + dialogBoxCreate("Invalid amount"); + } else { + props.mat.buy = parseFloat(buyAmt); + if (isNaN(props.mat.buy)) props.mat.buy = 0; + removePopup(props.popupId); + } + } + + function clearPurchase(): void { + props.mat.buy = 0; + removePopup(props.popupId); + } + + function onKeyDown(event: React.KeyboardEvent): void { + if (event.keyCode === 13) purchaseMaterial(); + } + + function onChange(event: React.ChangeEvent): void { + setBuyAmt(event.target.value); + } + + return (<> +

+ Enter the amount of {props.mat.name} you would like + to purchase per second. This material's cost changes constantly. +

+ + + + {props.industry.hasResearch("Bulk Purchasing") && } + ); +} diff --git a/src/Corporation/ui/Root.tsx b/src/Corporation/ui/Root.tsx index f7c3cb348..f41b93a27 100644 --- a/src/Corporation/ui/Root.tsx +++ b/src/Corporation/ui/Root.tsx @@ -7,7 +7,6 @@ import { IPlayer } from "../../PersonObjects/IPlayer"; interface IProps { corp: any; - eventHandler: any; routing: any; player: IPlayer; } @@ -15,8 +14,8 @@ interface IProps { export function CorporationRoot(props: IProps): React.ReactElement { return (
- - + +
) } diff --git a/src/Corporation/ui/SellMaterialPopup.tsx b/src/Corporation/ui/SellMaterialPopup.tsx new file mode 100644 index 000000000..1859ed103 --- /dev/null +++ b/src/Corporation/ui/SellMaterialPopup.tsx @@ -0,0 +1,129 @@ +import React, { useState } from 'react'; +import { Warehouse } from "../Warehouse"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; +import { createElement } from "../../../utils/uiHelpers/createElement"; +import { removePopup } from "../../ui/React/createPopup"; +import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement"; +import { clearSelector } from "../../../utils/uiHelpers/clearSelector"; +import { getSelectText, + getSelectValue } from "../../../utils/uiHelpers/getSelectData"; + +function initialPrice(mat: any): string { + let val = mat.sCost ? mat.sCost : ''; + if (mat.marketTa2) { + val += " (Market-TA.II)"; + } else if (mat.marketTa1) { + val += " (Market-TA.I)"; + } + return val; +} + +interface IProps { + mat: any; + corp: any; + popupId: string; +} + +// Create a popup that let the player manage sales of a material +export function SellMaterialPopup(props: IProps): React.ReactElement { + const [amt, setAmt] = useState(props.mat.sllman[1] ? props.mat.sllman[1] : ''); + const [price, setPrice] = useState(initialPrice(props.mat)); + + function sellMaterial(): void { + let p = price; + let qty = amt; + if(p === '') p = '0'; + if(qty === '') qty = '0'; + let cost = p.replace(/\s+/g, ''); + cost = cost.replace(/[^-()\d/*+.MP]/g, ''); //Sanitize cost + let temp = cost.replace(/MP/g, props.mat.bCost); + try { + temp = eval(temp); + } catch(e) { + dialogBoxCreate("Invalid value or expression for sell price field: " + e); + return; + } + + if (temp == null || isNaN(parseFloat(temp))) { + dialogBoxCreate("Invalid value or expression for sell price field"); + return; + } + + if (cost.includes("MP")) { + props.mat.sCost = cost; //Dynamically evaluated + } else { + props.mat.sCost = temp; + } + + //Parse quantity + if (qty.includes("MAX") || qty.includes("PROD")) { + let q = qty.replace(/\s+/g, ''); + q = q.replace(/[^-()\d/*+.MAXPROD]/g, ''); + let tempQty = q.replace(/MAX/g, '1'); + tempQty = tempQty.replace(/PROD/g, '1'); + try { + tempQty = eval(tempQty); + } catch(e) { + dialogBoxCreate("Invalid value or expression for sell price field: " + e); + return; + } + + if (tempQty == null || isNaN(parseFloat(tempQty))) { + dialogBoxCreate("Invalid value or expression for sell price field"); + return; + } + + props.mat.sllman[0] = true; + props.mat.sllman[1] = q; //Use sanitized input + } else if (isNaN(parseFloat(qty))) { + dialogBoxCreate("Invalid value for sell quantity field! Must be numeric or 'MAX'"); + return; + } else { + let q = parseFloat(qty); + if (isNaN(q)) {q = 0;} + if (q === 0) { + props.mat.sllman[0] = false; + props.mat.sllman[1] = 0; + } else { + props.mat.sllman[0] = true; + props.mat.sllman[1] = q; + } + } + + removePopup(props.popupId); + } + + function onAmtChange(event: React.ChangeEvent): void { + setAmt(event.target.value); + } + + function onPriceChange(event: React.ChangeEvent): void { + setPrice(event.target.value); + } + + function onKeyDown(event: React.KeyboardEvent): void { + if (event.keyCode === 13) sellMaterial(); + } + + return (<> +

+Enter the maximum amount of {props.mat.name} you would like +to sell per second, as well as the price at which you would +like to sell at.

+If the sell amount is set to 0, then the material will not be sold. If the sell price +if set to 0, then the material will be discarded

+Setting the sell amount to 'MAX' will result in you always selling the +maximum possible amount of the material.

+When setting the sell amount, you can use the 'PROD' variable to designate a dynamically +changing amount that depends on your production. For example, if you set the sell amount +to 'PROD-5' then you will always sell 5 less of the material than you produce.

+When setting the sell price, you can use the 'MP' variable to designate a dynamically +changing price that depends on the market price. For example, if you set the sell price +to 'MP+10' then it will always be sold at $10 above the market price. +

+
+ + + + ); +} diff --git a/src/Corporation/ui/SellProductPopup.tsx b/src/Corporation/ui/SellProductPopup.tsx new file mode 100644 index 000000000..72ea3556a --- /dev/null +++ b/src/Corporation/ui/SellProductPopup.tsx @@ -0,0 +1,170 @@ +import React, { useState } from 'react'; +import { Warehouse } from "../Warehouse"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; +import { createElement } from "../../../utils/uiHelpers/createElement"; +import { removePopup } from "../../ui/React/createPopup"; +import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement"; +import { clearSelector } from "../../../utils/uiHelpers/clearSelector"; +import { getSelectText, + getSelectValue } from "../../../utils/uiHelpers/getSelectData"; +import { Cities } from "../../Locations/Cities"; + +function initialPrice(product: any): string { + let val = product.sCost ? product.sCost : ''; + if (product.marketTa2) { + val += " (Market-TA.II)"; + } else if (product.marketTa1) { + val += " (Market-TA.I)"; + } + return val; +} + +interface IProps { + product: any; + city: string; + popupId: string; +} + +// Create a popup that let the player manage sales of a material +export function SellProductPopup(props: IProps): React.ReactElement { + const [checked, setChecked] = useState(true); + const [iQty, setQty] = useState(props.product.sllman[props.city][1] ? props.product.sllman[props.city][1] : ''); + const [px, setPx] = useState(initialPrice(props.product)); + + function onCheckedChange(event: React.ChangeEvent): void { + setChecked(event.target.checked); + } + + function sellProduct(): void { + //Parse price + if (px.includes("MP")) { + //Dynamically evaluated quantity. First test to make sure its valid + //Sanitize input, then replace dynamic variables with arbitrary numbers + var price = px.replace(/\s+/g, ''); + price = price.replace(/[^-()\d/*+.MP]/g, ''); + var temp = price.replace(/MP/g, '1'); + try { + temp = eval(temp); + } catch(e) { + dialogBoxCreate("Invalid value or expression for sell quantity field: " + e); + return; + } + if (temp == null || isNaN(parseFloat(temp))) { + dialogBoxCreate("Invalid value or expression for sell quantity field."); + return; + } + props.product.sCost = price; //Use sanitized price + } else { + var cost = parseFloat(px); + if (isNaN(cost)) { + dialogBoxCreate("Invalid value for sell price field"); + return; + } + props.product.sCost = cost; + } + + // Array of all cities. Used later + const cities = Object.keys(Cities); + + // Parse quantity + if (iQty.includes("MAX") || iQty.includes("PROD")) { + //Dynamically evaluated quantity. First test to make sure its valid + var qty = iQty.replace(/\s+/g, ''); + qty = qty.replace(/[^-()\d/*+.MAXPROD]/g, ''); + var temp = qty.replace(/MAX/g, '1'); + temp = temp.replace(/PROD/g, '1'); + try { + temp = eval(temp); + } catch(e) { + dialogBoxCreate("Invalid value or expression for sell price field: " + e); + return; + } + + if (temp == null || isNaN(parseFloat(temp))) { + dialogBoxCreate("Invalid value or expression for sell price field"); + return; + } + if (checked) { + for (let i = 0; i < cities.length; ++i) { + const tempCity = cities[i]; + props.product.sllman[tempCity][0] = true; + props.product.sllman[tempCity][1] = qty; //Use sanitized input + } + } else { + props.product.sllman[props.city][0] = true; + props.product.sllman[props.city][1] = qty; //Use sanitized input + } + } else if (isNaN(parseFloat(iQty))) { + dialogBoxCreate("Invalid value for sell quantity field! Must be numeric"); + return; + } else { + let qty = parseFloat(iQty); + if (isNaN(qty)) {qty = 0;} + if (qty === 0) { + if (checked) { + for (let i = 0; i < cities.length; ++i) { + const tempCity = cities[i]; + props.product.sllman[tempCity][0] = false; + props.product.sllman[tempCity][1] = ''; + } + } else { + props.product.sllman[props.city][0] = false; + props.product.sllman[props.city][1] = ''; + } + } else { + if (checked) { + for (let i = 0; i < cities.length; ++i) { + const tempCity = cities[i]; + props.product.sllman[tempCity][0] = true; + props.product.sllman[tempCity][1] = qty; + } + } else { + props.product.sllman[props.city][0] = true; + props.product.sllman[props.city][1] = qty; + } + } + } + + removePopup(props.popupId); + } + + function onAmtChange(event: React.ChangeEvent): void { + setQty(event.target.value); + } + + function onPriceChange(event: React.ChangeEvent): void { + setPx(event.target.value); + } + + function onKeyDown(event: React.KeyboardEvent): void { + if (event.keyCode === 13) sellProduct(); + } + + return (<> +

+Enter the maximum amount of {props.product.name} you would like +to sell per second, as well as the price at which you would like to +sell it at.

+If the sell amount is set to 0, then the product will not be sold. If the +sell price is set to 0, then the product will be discarded.

+Setting the sell amount to 'MAX' will result in you always selling the +maximum possible amount of the material.

+When setting the sell amount, you can use the 'PROD' variable to designate a +dynamically changing amount that depends on your production. For example, +if you set the sell amount to 'PROD-1' then you will always sell 1 less of +the material than you produce.

+When setting the sell price, you can use the 'MP' variable to set a +dynamically changing price that depends on the Product's estimated +market price. For example, if you set it to 'MP*5' then it +will always be sold at five times the estimated market price. +

+
+ + + +
+ + +
+ ); +}