diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts index bfa6be10e..3d77b76a1 100644 --- a/src/Corporation/Actions.ts +++ b/src/Corporation/Actions.ts @@ -4,8 +4,12 @@ import { IndustryStartingCosts } from "./IndustryData"; import { Industry } from "./Industry"; import { CorporationConstants } from "./data/Constants"; import { OfficeSpace } from "./OfficeSpace"; +import { Material } from "./Material"; +import { Product } from "./Product"; +import { Warehouse } from "./Warehouse"; import { CorporationUnlockUpgrade } from "./data/CorporationUnlockUpgrades"; import { CorporationUpgrade } from "./data/CorporationUpgrades"; +import { Cities } from "../Locations/Cities"; export function NewIndustry(corporation: ICorporation, industry: string, name: string): void { for (let i = 0; i < corporation.divisions.length; ++i) { @@ -70,4 +74,148 @@ export function IssueDividends(corporation: ICorporation, percent: number): void } corporation.dividendPercentage = percent*100; +} + +export function SellMaterial(mat: Material, amt: string, price: string): void { + if(price === '') price = '0'; + if(amt === '') amt = '0'; + let cost = price.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) { + throw new Error("Invalid value or expression for sell price field: " + e); + } + + if (temp == null || isNaN(parseFloat(temp))) { + throw new Error("Invalid value or expression for sell price field"); + } + + if (cost.includes("MP")) { + mat.sCost = cost; //Dynamically evaluated + } else { + mat.sCost = temp; + } + + //Parse quantity + if (amt.includes("MAX") || amt.includes("PROD")) { + let q = amt.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) { + throw new Error("Invalid value or expression for sell price field: " + e); + } + + if (tempQty == null || isNaN(parseFloat(tempQty))) { + throw new Error("Invalid value or expression for sell price field"); + } + + mat.sllman[0] = true; + mat.sllman[1] = q; //Use sanitized input + } else if (isNaN(parseFloat(amt))) { + throw new Error("Invalid value for sell quantity field! Must be numeric or 'MAX'"); + } else { + let q = parseFloat(amt); + if (isNaN(q)) {q = 0;} + if (q === 0) { + mat.sllman[0] = false; + mat.sllman[1] = 0; + } else { + mat.sllman[0] = true; + mat.sllman[1] = q; + } + } +} + +export function SellProduct(product: Product, city: string, amt: string, price: string, all: boolean): void { + //Parse price + if (price.includes("MP")) { + //Dynamically evaluated quantity. First test to make sure its valid + //Sanitize input, then replace dynamic variables with arbitrary numbers + price = price.replace(/\s+/g, ''); + price = price.replace(/[^-()\d/*+.MP]/g, ''); + let temp = price.replace(/MP/g, '1'); + try { + temp = eval(temp); + } catch(e) { + throw new Error("Invalid value or expression for sell quantity field: " + e); + } + if (temp == null || isNaN(parseFloat(temp))) { + throw new Error("Invalid value or expression for sell quantity field."); + } + product.sCost = price; //Use sanitized price + } else { + const cost = parseFloat(price); + if (isNaN(cost)) { + throw new Error("Invalid value for sell price field"); + } + product.sCost = cost; + } + + // Array of all cities. Used later + const cities = Object.keys(Cities); + + // Parse quantity + if (amt.includes("MAX") || amt.includes("PROD")) { + //Dynamically evaluated quantity. First test to make sure its valid + let qty = amt.replace(/\s+/g, ''); + qty = qty.replace(/[^-()\d/*+.MAXPROD]/g, ''); + let temp = qty.replace(/MAX/g, '1'); + temp = temp.replace(/PROD/g, '1'); + try { + temp = eval(temp); + } catch(e) { + throw new Error("Invalid value or expression for sell price field: " + e); + } + + if (temp == null || isNaN(parseFloat(temp))) { + throw new Error("Invalid value or expression for sell price field"); + } + if (all) { + 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(parseFloat(amt))) { + throw new Error("Invalid value for sell quantity field! Must be numeric"); + } else { + let qty = parseFloat(amt); + if (isNaN(qty)) {qty = 0;} + if (qty === 0) { + if (all) { + for (let i = 0; i < cities.length; ++i) { + const tempCity = cities[i]; + product.sllman[tempCity][0] = false; + product.sllman[tempCity][1] = ''; + } + } else { + product.sllman[city][0] = false; + product.sllman[city][1] = ''; + } + } else { + if (all) { + 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; + } + } + } +} + +export function SetSmartSupply(warehouse: Warehouse, smartSupply: boolean): void { + warehouse.smartSupplyEnabled = smartSupply; } \ No newline at end of file diff --git a/src/Corporation/ui/ExpandNewCityPopup.tsx b/src/Corporation/ui/ExpandNewCityPopup.tsx index f987103f4..f40807fcc 100644 --- a/src/Corporation/ui/ExpandNewCityPopup.tsx +++ b/src/Corporation/ui/ExpandNewCityPopup.tsx @@ -23,7 +23,7 @@ export function ExpandNewCityPopup(props: IProps): React.ReactElement { try { NewCity(props.corp, props.division, dropdown.current.value); } catch(err) { - dialogBoxCreate(err); + dialogBoxCreate(err+''); return; } diff --git a/src/Corporation/ui/IndustryWarehouse.tsx b/src/Corporation/ui/IndustryWarehouse.tsx index 521115f72..cce46cefb 100644 --- a/src/Corporation/ui/IndustryWarehouse.tsx +++ b/src/Corporation/ui/IndustryWarehouse.tsx @@ -25,6 +25,8 @@ import { ICorporation } from "../ICorporation"; import { IIndustry } from "../IIndustry"; import { CorporationRouting } from "./Routing"; import { IPlayer } from "../../PersonObjects/IPlayer"; +import { SetSmartSupply } from "../Actions"; + interface IProductProps { corp: ICorporation; @@ -514,7 +516,7 @@ export function IndustryWarehouse(props: IProps): React.ReactElement { const smartSupplyCheckboxId = "cmpy-mgmt-smart-supply-checkbox"; function smartSupplyOnChange(e: React.ChangeEvent): void { if(warehouse === 0) return; - warehouse.smartSupplyEnabled = e.target.checked; + SetSmartSupply(warehouse, e.target.checked); corp.rerender(props.player); } diff --git a/src/Corporation/ui/IssueDividendsPopup.tsx b/src/Corporation/ui/IssueDividendsPopup.tsx index eb022d58e..881af18fa 100644 --- a/src/Corporation/ui/IssueDividendsPopup.tsx +++ b/src/Corporation/ui/IssueDividendsPopup.tsx @@ -20,7 +20,7 @@ export function IssueDividendsPopup(props: IProps): React.ReactElement { try { IssueDividends(props.corp, percent/100); } catch(err) { - dialogBoxCreate(err); + dialogBoxCreate(err+''); } removePopup(props.popupId); diff --git a/src/Corporation/ui/LevelableUpgrade.tsx b/src/Corporation/ui/LevelableUpgrade.tsx index a1a9e5170..358bc13bd 100644 --- a/src/Corporation/ui/LevelableUpgrade.tsx +++ b/src/Corporation/ui/LevelableUpgrade.tsx @@ -29,7 +29,7 @@ export function LevelableUpgrade(props: IProps): React.ReactElement { try { LevelUpgrade(props.corp, props.upgrade); } catch(err) { - dialogBoxCreate(err); + dialogBoxCreate(err+''); } props.corp.rerender(props.player); } diff --git a/src/Corporation/ui/NewIndustryPopup.tsx b/src/Corporation/ui/NewIndustryPopup.tsx index a841aa295..8fa4f0aff 100644 --- a/src/Corporation/ui/NewIndustryPopup.tsx +++ b/src/Corporation/ui/NewIndustryPopup.tsx @@ -28,7 +28,7 @@ export function NewIndustryPopup(props: IProps): React.ReactElement { try { NewIndustry(props.corp, industry, name); } catch(err) { - dialogBoxCreate(err); + dialogBoxCreate(err+''); return; } diff --git a/src/Corporation/ui/SellMaterialPopup.tsx b/src/Corporation/ui/SellMaterialPopup.tsx index 4b4828ba0..b209a90e0 100644 --- a/src/Corporation/ui/SellMaterialPopup.tsx +++ b/src/Corporation/ui/SellMaterialPopup.tsx @@ -3,6 +3,7 @@ import { dialogBoxCreate } from "../../../utils/DialogBox"; import { removePopup } from "../../ui/React/createPopup"; import { ICorporation } from "../ICorporation"; import { Material } from "../Material"; +import { SellMaterial } from "../Actions"; function initialPrice(mat: Material): string { let val = mat.sCost ? mat.sCost+'' : ''; @@ -26,64 +27,10 @@ export function SellMaterialPopup(props: IProps): React.ReactElement { 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; - } + SellMaterial(props.mat, amt, price); + } catch(err) { + dialogBoxCreate(err+''); } removePopup(props.popupId); diff --git a/src/Corporation/ui/SellProductPopup.tsx b/src/Corporation/ui/SellProductPopup.tsx index 3c60fbc32..d04f13f60 100644 --- a/src/Corporation/ui/SellProductPopup.tsx +++ b/src/Corporation/ui/SellProductPopup.tsx @@ -3,6 +3,7 @@ import { dialogBoxCreate } from "../../../utils/DialogBox"; import { removePopup } from "../../ui/React/createPopup"; import { Cities } from "../../Locations/Cities"; import { Product } from "../Product"; +import { SellProduct } from "../Actions"; function initialPrice(product: Product): string { let val = product.sCost ? product.sCost+'' : ''; @@ -31,93 +32,10 @@ export function SellProductPopup(props: IProps): React.ReactElement { } 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 - let price = px.replace(/\s+/g, ''); - price = price.replace(/[^-()\d/*+.MP]/g, ''); - let 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 { - const 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 - let qty = iQty.replace(/\s+/g, ''); - qty = qty.replace(/[^-()\d/*+.MAXPROD]/g, ''); - let 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; - } - } + try { + SellProduct(props.product, props.city, iQty, px, checked); + } catch(err) { + dialogBoxCreate(err+''); } removePopup(props.popupId); diff --git a/src/Corporation/ui/UnlockUpgrade.tsx b/src/Corporation/ui/UnlockUpgrade.tsx index 785893d83..5b8db7059 100644 --- a/src/Corporation/ui/UnlockUpgrade.tsx +++ b/src/Corporation/ui/UnlockUpgrade.tsx @@ -23,7 +23,7 @@ export function UnlockUpgrade(props: IProps): React.ReactElement { try { UU(props.corp, props.upgradeData); } catch(err) { - dialogBoxCreate(err); + dialogBoxCreate(err+''); } props.corp.rerender(props.player); } diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index cdba2f86d..be376d144 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -25,7 +25,10 @@ import { NewCity, UnlockUpgrade, LevelUpgrade, - IssueDividends } from "./Corporation/Actions"; + IssueDividends, + SellMaterial, + SellProduct, + SetSmartSupply } from "./Corporation/Actions"; import { CorporationUnlockUpgrades } from "./Corporation/data/CorporationUnlockUpgrades"; import { CorporationUpgrades } from "./Corporation/data/CorporationUpgrades"; import { @@ -563,6 +566,39 @@ function NetscriptFunctions(workerScript) { return Augmentations[name]; } + function getDivision(divisionName) { + const division = Player.corporation.divisions.find(div => div.name === divisionName); + if(division === undefined) + throw new Error(`No division named '${divisionName}'`); + return division; + } + + function getWarehouse(divisionName, cityName) { + const division = getDivision(divisionName); + if(!(cityName in division.warehouses)) + throw new Error(`Invalid city name '${cityName}'`); + const warehouse = division.warehouses[cityName]; + if(warehouse === 0) + throw new Error(`${division.name} has not expanded to '${cityName}'`); + return warehouse; + } + + function getMaterial(divisionName, cityName, materialName) { + const warehouse = getWarehouse(divisionName, cityName); + const material = warehouse.materials[materialName]; + if(material === undefined) + throw new Error(`Invalid material name: '${materialName}'`); + return material; + } + + function getProduct(divisionName, productName) { + const division = getDivision(divisionName); + const product = division.products[productName]; + if(product === undefined) + throw new Error(`Invalid product name: '${productName}'`); + return product; + } + const runAfterReset = function(cbScript=null) { //Run a script after reset if (cbScript && isString(cbScript)) { @@ -4110,8 +4146,7 @@ function NetscriptFunctions(workerScript) { NewIndustry(Player.corporation, industryName, divisionName); }, expandCity: function(divisionName, cityName) { - const division = Player.corporation.divisions.find(div => div.name === divisionName); - if(division === undefined) throw new Error("No division named '${divisionName}'"); + const division = getDivision(divisionName); NewCity(Player.corporation, division, cityName); }, unlockUpgrade: function(upgradeName) { @@ -4129,6 +4164,18 @@ function NetscriptFunctions(workerScript) { issueDividends: function(percent) { IssueDividends(Player.corporation, percent); }, + sellMaterial: function(divisionName, cityName, materialName, amt, price) { + const material = getMaterial(divisionName, cityName, materialName); + SellMaterial(material, amt, price); + }, + sellProduct: function(divisionName, cityName, productName, amt, price, all) { + const product = getProduct(divisionName, productName); + SellProduct(product, cityName, amt, price, all); + }, + setSmartSupply: function(divisionName, cityName, enabled) { + const warehouse = getWarehouse(divisionName, cityName); + SetSmartSupply(warehouse, enabled); + }, }, // End Corporation API // Coding Contract API