Fix for invalid materials in warehouse (#660)

This commit is contained in:
Snarling 2023-07-05 19:36:22 -04:00 committed by GitHub
parent 26cdc502bf
commit 312e3eb71f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 30 deletions

@ -249,14 +249,26 @@ export function SetSmartSupplyOption(warehouse: Warehouse, material: Material, u
warehouse.smartSupplyOptions[material.name] = useOption;
}
export function BuyMaterial(material: Material, amt: number): void {
export function BuyMaterial(division: Division, material: Material, amt: number): void {
if (!isRelevantMaterial(material.name, division)) {
throw new Error(`${material.name} is not a relevant material for industry ${division.type}`);
}
if (isNaN(amt) || amt < 0) {
throw new Error(`Invalid amount '${amt}' to buy material '${material.name}'`);
}
material.buyAmount = amt;
}
export function BulkPurchase(corp: Corporation, warehouse: Warehouse, material: Material, amt: number): void {
export function BulkPurchase(
corp: Corporation,
division: Division,
warehouse: Warehouse,
material: Material,
amt: number,
): void {
if (!isRelevantMaterial(material.name, division)) {
throw new Error(`${material.name} is not a relevant material for industry ${division.type}`);
}
const matSize = MaterialInfo[material.name].size;
const maxAmount = (warehouse.size - warehouse.sizeUsed) / matSize;
if (isNaN(amt) || amt < 0) {

@ -16,27 +16,30 @@ import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { useRerender } from "../../../ui/React/hooks";
import { getRecordKeys } from "../../../Types/Record";
import { ButtonWithTooltip } from "../../../ui/Components/ButtonWithTooltip";
interface IProps {
interface ExportModalProps {
open: boolean;
onClose: () => void;
mat: Material;
}
// Create a popup that lets the player manage exports
export function ExportModal(props: IProps): React.ReactElement {
export function ExportModal(props: ExportModalProps): React.ReactElement {
const corp = useCorporation();
const [exportAmount, setExportAmount] = useState("");
const rerender = useRerender();
const possibleDivisions = [...corp.divisions.values()].filter((division: Division) => {
return isRelevantMaterial(props.mat.name, division);
});
if (possibleDivisions.length === 0) throw new Error("Export popup created with no divisions.");
const defaultDivision = possibleDivisions[0];
if (Object.keys(defaultDivision.warehouses).length === 0)
throw new Error("Export popup created in a division with no warehouses.");
const [targetDivision, setTargetDivision] = useState(defaultDivision);
const [targetCity, setTargetCity] = useState(CityName.Sector12);
const [exportAmount, setExportAmount] = useState("");
const rerender = useRerender();
// This weird assignment is used because ts thinks possibleDivisions[0] is always a division
const defaultDivision = possibleDivisions.length ? possibleDivisions[0] : null;
const [targetDivision, setTargetDivision] = useState<Division | null>(defaultDivision);
const possibleCities = targetDivision ? getRecordKeys(targetDivision.warehouses) : [];
const defaultCity = possibleCities.length ? possibleCities[0] : null;
const [targetCity, setTargetCity] = useState(defaultCity);
function onCityChange(event: SelectChangeEvent<CityName>): void {
setTargetCity(event.target.value as CityName);
@ -54,6 +57,7 @@ export function ExportModal(props: IProps): React.ReactElement {
function exportMaterial(): void {
try {
if (!targetDivision || !targetCity) return;
ExportMaterial(targetDivision, targetCity, props.mat, exportAmount);
} catch (err) {
dialogBoxCreate(err + "");
@ -75,9 +79,8 @@ export function ExportModal(props: IProps): React.ReactElement {
rerender();
}
const possibleCities = getRecordKeys(targetDivision.warehouses);
if (possibleCities.length > 0 && !possibleCities.includes(targetCity)) {
setTargetCity(possibleCities[0]);
if (targetCity && !possibleCities.includes(targetCity as CityName)) {
setTargetCity(possibleCities.length ? possibleCities[0] : null);
}
return (
@ -104,16 +107,14 @@ export function ExportModal(props: IProps): React.ReactElement {
<br />
For example: setting the amount "(EINV-20)/10" would try to export all except 20 of the material.
</Typography>
<Select onChange={onTargetDivisionChange} value={targetDivision.name}>
{[...corp.divisions.values()]
.filter((division) => isRelevantMaterial(props.mat.name, division))
.map((division) => (
<MenuItem key={division.name} value={division.name}>
{division.name}
</MenuItem>
))}
<Select onChange={onTargetDivisionChange} value={targetDivision?.name ?? ""}>
{possibleDivisions.map((division) => (
<MenuItem key={division.name} value={division.name}>
{division.name}
</MenuItem>
))}
</Select>
<Select onChange={onCityChange} value={targetCity}>
<Select onChange={onCityChange} value={targetCity ?? ""}>
{possibleCities.map((cityName) => (
<MenuItem key={cityName} value={cityName}>
{cityName}
@ -121,7 +122,12 @@ export function ExportModal(props: IProps): React.ReactElement {
))}
</Select>
<TextField placeholder="Export amount / s" onChange={onAmtChange} value={exportAmount} />
<Button onClick={exportMaterial}>Export</Button>
<ButtonWithTooltip
disabledTooltip={!targetDivision ? "No target division selected" : !targetCity ? "No target city selected" : ""}
onClick={exportMaterial}
>
Export
</ButtonWithTooltip>
<Typography>
Below is a list of all current exports of this material from this warehouse. Clicking on one of the exports
below will REMOVE that export.

@ -6,7 +6,7 @@ import { Material } from "../../Material";
import { formatMatPurchaseAmount, formatMoney } from "../../../ui/formatNumber";
import { BulkPurchase, BuyMaterial } from "../../Actions";
import { Modal } from "../../../ui/React/Modal";
import { useCorporation } from "../Context";
import { useCorporation, useDivision } from "../Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
@ -26,6 +26,7 @@ interface IBPProps {
function BulkPurchaseSection(props: IBPProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const [buyAmt, setBuyAmt] = useState("");
const [disabled, setDisabled] = useState(false);
@ -64,7 +65,7 @@ function BulkPurchaseSection(props: IBPProps): React.ReactElement {
function bulkPurchase(): void {
try {
BulkPurchase(corp, props.warehouse, props.mat, parseFloat(buyAmt));
BulkPurchase(corp, division, props.warehouse, props.mat, parseFloat(buyAmt));
} catch (err) {
dialogBoxCreate(err + "");
}
@ -110,12 +111,13 @@ interface IProps {
// Create a popup that lets the player purchase a Material
export function PurchaseMaterialModal(props: IProps): React.ReactElement {
const division = useDivision();
const [buyAmt, setBuyAmt] = useState(props.mat.buyAmount ? props.mat.buyAmount : 0);
function purchaseMaterial(): void {
if (buyAmt === null) return;
try {
BuyMaterial(props.mat, buyAmt);
BuyMaterial(division, props.mat, buyAmt);
} catch (err) {
dialogBoxCreate(err + "");
}

@ -416,24 +416,28 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
buyMaterial: (ctx) => (_divisionName, _cityName, _materialName, _amt) => {
checkAccess(ctx, CorpUnlockName.WarehouseAPI);
const divisionName = helpers.string(ctx, "divisionName", _divisionName);
const division = getCorporation().divisions.get(divisionName);
if (!division) throw helpers.makeRuntimeErrorMsg(ctx, `No division with provided name ${divisionName}`);
const cityName = getEnumHelper("CityName").nsGetMember(ctx, _cityName);
const materialName = getEnumHelper("CorpMaterialName").nsGetMember(ctx, _materialName, "materialName");
const amt = helpers.number(ctx, "amt", _amt);
if (amt < 0 || !Number.isFinite(amt))
throw new Error("Invalid value for amount field! Must be numeric and greater than 0");
const material = getMaterial(divisionName, cityName, materialName);
BuyMaterial(material, amt);
BuyMaterial(division, material, amt);
},
bulkPurchase: (ctx) => (_divisionName, _cityName, _materialName, _amt) => {
checkAccess(ctx, CorpUnlockName.WarehouseAPI);
const divisionName = helpers.string(ctx, "divisionName", _divisionName);
const division = getCorporation().divisions.get(divisionName);
if (!division) throw helpers.makeRuntimeErrorMsg(ctx, `No division with provided name ${divisionName}`);
const corporation = getCorporation();
const cityName = getEnumHelper("CityName").nsGetMember(ctx, _cityName);
const materialName = getEnumHelper("CorpMaterialName").nsGetMember(ctx, _materialName, "materialName");
const amt = helpers.number(ctx, "amt", _amt);
const warehouse = getWarehouse(divisionName, cityName);
const material = getMaterial(divisionName, cityName, materialName);
BulkPurchase(corporation, warehouse, material, amt);
BulkPurchase(corporation, division, warehouse, material, amt);
},
makeProduct:
(ctx) =>