diff --git a/css/companymanagement.scss b/css/companymanagement.scss
index 7a8944a2b..e93dd331b 100644
--- a/css/companymanagement.scss
+++ b/css/companymanagement.scss
@@ -10,7 +10,8 @@
#cmpy-mgmt-container p,
#cmpy-mgmt-container a,
-#cmpy-mgmt-container div {
+#cmpy-mgmt-container div,
+#cmpy-mgmt-container br {
font-size: $defaultFontSize * 0.8125;
}
diff --git a/src/Constants.ts b/src/Constants.ts
index 33022df26..1d48c94a2 100644
--- a/src/Constants.ts
+++ b/src/Constants.ts
@@ -297,9 +297,11 @@ export let CONSTANTS: IMap Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}
+
Material Production: {numeralWrapper.format(division.getOfficeProductivity(office), "0.000")}
The base amount of material this office can produce. Does not include
@@ -314,9 +314,12 @@ export class IndustryOffice extends BaseReactComponent {
+
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
@@ -325,15 +328,21 @@ export class IndustryOffice extends BaseReactComponent {
+
Business Multiplier: x{numeralWrapper.format(division.getBusinessFactor(office), "0.000")}
The effect this office's 'Business' employees has on boosting sales
Market-TA.II
` +
`If you sell at ${numeralWrapper.formatMoney(sCost)}, ` +
- `then you will sell ${formatNumber(markup, 5)}x as much compared ` +
+ `then you will sell ${numeralWrapper.format(markup, "0.00000")}x as much compared ` +
`to if you sold at market price.`;
}
updateTa2Text();
- createPopup(popupId, [ta1, ta2Text, ta2Input, closeBtn]);
+
+ // 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 Material 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: mat.marketTa2,
+ id: useTa2AutoSaleId,
+ margin: "3px",
+ type: "checkbox",
+ changeListener: (e) => {
+ mat.marketTa2 = e.target.checked;
+ }
+ });
+ useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
+ useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
+
+ createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
} else {
// Market-TA.I only
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
@@ -775,6 +805,7 @@ export class CorporationEventHandler {
display:"inline-block",
innerText: "Confirm",
clickListener: () => {
+ if (citySelector.length <= 0) { return false; }
let city = citySelector.options[citySelector.selectedIndex].value;
if (this.corp.funds.lt(OfficeInitialCost)) {
dialogBoxCreate("You don't have enough company funds to open a new office!");
@@ -921,8 +952,110 @@ export class CorporationEventHandler {
return false;
}
+ // Create a popup that lets the player use the Market TA research for Products
+ createProductMarketTaPopup(product, industry) {
+ const corp = this.corp;
+
+ 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);
+
+ createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
+ } else {
+ // Market-TA.I only
+ createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
+ }
+ }
+
// Create a popup that lets the player purchase a Material
- createPurchaseMaterialPopup(mat, industry) {
+ createPurchaseMaterialPopup(mat, industry, warehouse) {
const corp = this.corp;
const purchasePopupId = "cmpy-mgmt-material-purchase-popup";
@@ -980,15 +1113,20 @@ export class CorporationEventHandler {
let bulkPurchaseCostTxt = createElement("p");
function updateBulkPurchaseText(amount) {
- const cost = parseFloat(amount) * mat.bCost;
- if (isNaN(cost)) {
- dialogBoxCreate(`Bulk Purchase Cost calculated to be NaN. This is either due to ` +
- `invalid input, or it is a bug (in which case you should report to dev)`);
- return;
- }
+ const parsedAmt = parseFloat(amount);
+ const cost = parsedAmt * mat.bCost;
- bulkPurchaseCostTxt.innerText = `Purchasing ${numeralWrapper.format(amt, "0,0.00")} of ` +
- `${mat.name} will cost ${numeralWrapper.formatMoney(cost)}`;
+ 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;
@@ -998,7 +1136,7 @@ export class CorporationEventHandler {
type: "number",
onkeyup: (e) => {
e.preventDefault();
- updateBulkPurchaseText();
+ updateBulkPurchaseText(e.target.value);
if (e.keyCode === KEY.ENTER) {bulkPurchaseConfirmBtn.click();}
}
});
@@ -1007,7 +1145,15 @@ export class CorporationEventHandler {
class: "std-button",
innerText: "Confirm Bulk Purchase",
clickListener: () => {
- const amount = parseFloat(input.value);
+ 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 {
@@ -1065,9 +1211,18 @@ export class CorporationEventHandler {
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: mat.sCost ? mat.sCost : null, placeholder: "Sell price",
+ value: inputButtonInitValue,
+ placeholder: "Sell price",
onkeyup: (e) => {
e.preventDefault();
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
@@ -1179,16 +1334,41 @@ export class CorporationEventHandler {
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: product.sCost ? product.sCost : null,
+ 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",
@@ -1220,7 +1400,10 @@ export class CorporationEventHandler {
product.sCost = cost;
}
- //Parse quantity
+ // Array of all cities. Used later
+ const cities = Object.values(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, '');
@@ -1238,8 +1421,16 @@ export class CorporationEventHandler {
dialogBoxCreate("Invalid value or expression for sell price field");
return false;
}
- product.sllman[city][0] = true;
- product.sllman[city][1] = qty; //Use sanitized input
+ 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;
@@ -1247,10 +1438,25 @@ export class CorporationEventHandler {
var qty = parseFloat(inputQty.value);
if (isNaN(qty)) {qty = 0;}
if (qty === 0) {
- product.sllman[city][0] = false;
+ 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 {
- product.sllman[city][0] = true;
- product.sllman[city][1] = qty;
+ 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;
+ }
}
}
@@ -1259,9 +1465,12 @@ export class CorporationEventHandler {
return false;
}
});
- const cancelBtn = createPopupCloseButton(popupId, { innerText: "Cancel" });
+ const cancelBtn = createPopupCloseButton(popupId, { class: "std-button" });
- createPopup(popupId, [txt, inputQty, inputPx, confirmBtn, cancelBtn]);
+ const linebreak1 = createElement("br");
+
+ createPopup(popupId, [txt, inputQty, inputPx, confirmBtn, cancelBtn, linebreak1,
+ checkboxDiv]);
inputQty.focus();
}
diff --git a/src/Corporation/ui/IndustryOffice.jsx b/src/Corporation/ui/IndustryOffice.jsx
index 91d234a80..54ce1dfa7 100644
--- a/src/Corporation/ui/IndustryOffice.jsx
+++ b/src/Corporation/ui/IndustryOffice.jsx
@@ -305,7 +305,7 @@ export class IndustryOffice extends BaseReactComponent {
+ }
{
vechain &&
-
+ }
{
vechain &&
-
+ }
{EmployeePositions.Operations} ({this.state.numOperations})
diff --git a/src/Corporation/ui/IndustryWarehouse.jsx b/src/Corporation/ui/IndustryWarehouse.jsx
index e13bb3cf7..8b69638f1 100644
--- a/src/Corporation/ui/IndustryWarehouse.jsx
+++ b/src/Corporation/ui/IndustryWarehouse.jsx
@@ -3,13 +3,13 @@
import React from "react";
import { BaseReactComponent } from "./BaseReactComponent";
-import { Material } from "../Material";
-import { Product } from "../Product";
-
-import { Warehouse,
+import { OfficeSpace,
WarehouseInitialCost,
WarehouseUpgradeBaseCost,
ProductProductionCostRatio } from "../Corporation";
+import { Material } from "../Material";
+import { Product } from "../Product";
+import { Warehouse } from "../Warehouse";
import { numeralWrapper } from "../../ui/numeralFormat";
@@ -45,7 +45,12 @@ function ProductComponent(props) {
sellButtonText = "Sell (0.000/0.000)";
}
- if (product.sCost) {
+ if (product.marketTa2) {
+ sellButtonText += (" @ " + numeralWrapper.formatMoney(product.marketTa2Price[city]));
+ } else if (product.marketTa1) {
+ const markupLimit = product.rat / product.mku;
+ sellButtonText += (" @ " + numeralWrapper.formatMoney(product.pCost + markupLimit));
+ } else if (product.sCost) {
if (isString(product.sCost)) {
sellButtonText += (" @ " + product.sCost);
} else {
@@ -55,14 +60,17 @@ function ProductComponent(props) {
const sellButtonOnClick = eventHandler.createSellProductPopup.bind(eventHandler, product, city);
// Limit Production button
- const limitProductionButtonText = "Limit Production";
+ let limitProductionButtonText = "Limit Production";
if (product.prdman[city][0]) {
limitProductionButtonText += " (" + numeralWrapper.format(product.prdman[city][1], nf) + ")";
}
const limitProductionButtonOnClick = eventHandler.createLimitProductProdutionPopup.bind(eventHandler, product, city);
// Discontinue Button
- const discontinueButtonOnClick = eventHandler.createDiscontinueProductPopup.bind(eventHandler, product);
+ const discontinueButtonOnClick = eventHandler.createDiscontinueProductPopup.bind(eventHandler, product, division);
+
+ // Market TA button
+ const marketTaButtonOnClick = eventHandler.createProductMarketTaPopup.bind(eventHandler, product, division);
// Unfinished Product
if (!product.fin) {
@@ -83,6 +91,12 @@ function ProductComponent(props) {
+ {
+ division.hasResearch("Market-TA.I") &&
+
+ }
)
@@ -139,7 +153,7 @@ function ProductComponent(props) {
- Est. Market Price: {numeralWrapper.formatMoney(product.pCost + product.rat / product.mku)}
+ Est. Market Price: {numeralWrapper.formatMoney(product.pCost)}
An estimate of how much consumers are willing to pay for this product.
Setting the sale price above this may result in less sales. Setting the sale price below this may result
@@ -157,6 +171,12 @@ function ProductComponent(props) {
+ {
+ division.hasResearch("Market-TA.I") &&
+
+ }
)
@@ -167,9 +187,14 @@ function MaterialComponent(props) {
const corp = props.corp;
const division = props.division;
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)) {
+ throw new Error(`Could not get OfficeSpace object for this city (${city})`);
+ }
// Numeraljs formatter
const nf = "0.000";
@@ -195,7 +220,7 @@ function MaterialComponent(props) {
// Purchase material button
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nf)})`;
const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button";
- const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division);
+ const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division, warehouse);
// Export material button
const exportButtonOnClick = eventHandler.createExportMaterialPopup.bind(eventHandler, mat);
@@ -209,10 +234,12 @@ function MaterialComponent(props) {
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);
- } else if (isString(mat.sCost)) {
+ if (mat.marketTa2) {
+ sellButtonText += " @ " + numeralWrapper.formatMoney(mat.marketTa2Price);
+ } else if (mat.marketTa1) {
+ sellButtonText += " @ " + numeralWrapper.formatMoney(mat.bCost + markupLimit);
+ } else if (mat.sCost) {
+ if (isString(mat.sCost)) {
var sCost = mat.sCost.replace(/MP/g, mat.bCost);
sellButtonText += " @ " + numeralWrapper.formatMoney(eval(sCost));
} else {
@@ -225,7 +252,7 @@ function MaterialComponent(props) {
const sellButtonOnClick = eventHandler.createSellMaterialPopup.bind(eventHandler, mat);
// Market TA button
- const marketTaButtonOnClick = eventHandler.createMarketTaPopup.bind(eventHandler, mat, division);
+ const marketTaButtonOnClick = eventHandler.createMaterialMarketTaPopup.bind(eventHandler, mat, division);
return (
${aug.name}
`,
- `Cost: ${numeralWrapper.formatMoney(aug.baseCost)}
`,
+ `Cost: ${numeralWrapper.formatMoney(aug.startingCost)}
`,
`${aug.info}`
].join(" "),
padding: "2px",
clickListener: () => {
- if (p.canAfford(aug.baseCost)) {
- p.loseMoney(aug.baseCost);
+ if (p.canAfford(aug.startingCost)) {
+ p.loseMoney(aug.startingCost);
sleeve.installAugmentation(aug);
dialogBoxCreate(`Installed ${aug.name} on Duplicate Sleeve!`, false)
removeElementById(popupId);
diff --git a/tsconfig.json b/tsconfig.json
index 831898523..3c82af0b1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,7 +2,7 @@
"compilerOptions": {
"baseUrl" : ".",
"jsx": "react",
- "lib" : ["es2016", "dom"],
+ "lib" : ["es2016", "dom", "es2017.object"],
"module": "commonjs",
"target": "es6",
"sourceMap": true,