bitburner-src/src/Corporation/Actions.ts

588 lines
22 KiB
TypeScript
Raw Normal View History

import { Player } from "@player";
import { CorpResearchName, CorpSmartSupplyOption } from "@nsdefs";
import { MaterialInfo } from "./MaterialInfo";
2022-09-23 11:54:04 +02:00
import { Corporation } from "./Corporation";
import { IndustryResearchTrees, IndustriesData } from "./data/IndustryData";
import { Division } from "./Division";
import * as corpConstants from "./data/Constants";
2021-09-02 04:16:48 +02:00
import { OfficeSpace } from "./OfficeSpace";
2021-09-02 06:36:33 +02:00
import { Material } from "./Material";
import { Product } from "./Product";
import { Warehouse } from "./Warehouse";
import { IndustryType } from "@enums";
2021-09-10 08:17:55 +02:00
import { ResearchMap } from "./ResearchMap";
import { isRelevantMaterial } from "./ui/Helpers";
import { CityName } from "@enums";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { getRecordValues } from "../Types/Record";
import { sellSharesFailureReason, buybackSharesFailureReason, issueNewSharesFailureReason } from "./helpers";
2021-09-02 04:16:48 +02:00
export function NewDivision(corporation: Corporation, industry: IndustryType, name: string): void {
if (corporation.divisions.size >= corporation.maxDivisions)
throw new Error(`Cannot expand into ${industry} industry, too many divisions!`);
if (corporation.divisions.has(name)) throw new Error(`Division name ${name} is already in use!`);
// "Overview" is forbidden as a division name, see CorporationRoot.tsx for why this would cause issues.
if (name === "Overview") throw new Error(`"Overview" is a forbidden division name.`);
2021-09-02 04:16:48 +02:00
const data = IndustriesData[industry];
if (!data) throw new Error(`Invalid industry: '${industry}'`);
const cost = data.startingCost;
2021-11-12 03:35:26 +01:00
if (corporation.funds < cost) {
2021-09-09 05:47:34 +02:00
throw new Error("Not enough money to create a new division in this industry");
2021-09-05 01:09:30 +02:00
} else if (name === "") {
throw new Error("New division must have a name!");
} else {
2021-11-12 03:35:26 +01:00
corporation.funds = corporation.funds - cost;
corporation.divisions.set(
name,
new Division({
2021-09-05 01:09:30 +02:00
corp: corporation,
name: name,
type: industry,
}),
);
}
2021-09-02 04:16:48 +02:00
}
export function removeDivision(corporation: Corporation, name: string) {
if (!corporation.divisions.has(name)) throw new Error("There is no division called " + name);
corporation.divisions.delete(name);
// We also need to remove any exports that were pointing to the old division
for (const otherDivision of corporation.divisions.values()) {
for (const warehouse of getRecordValues(otherDivision.warehouses)) {
for (const material of getRecordValues(warehouse.materials)) {
// Work backwards through exports array so splicing doesn't affect the loop
for (let i = material.exports.length - 1; i >= 0; i--) {
if (material.exports[i].division === name) material.exports.splice(i, 1);
}
}
}
}
}
export function purchaseOffice(corporation: Corporation, division: Division, city: CityName): void {
if (corporation.funds < corpConstants.officeInitialCost) {
2021-09-09 05:47:34 +02:00
throw new Error("You don't have enough company funds to open a new office!");
2021-09-05 01:09:30 +02:00
}
if (division.offices[city]) {
throw new Error(`You have already expanded into ${city} for ${division.name}`);
}
corporation.addNonIncomeFunds(-corpConstants.officeInitialCost);
division.offices[city] = new OfficeSpace({
city: city,
size: corpConstants.officeInitialSize,
});
2021-09-02 04:16:48 +02:00
}
2022-09-20 12:47:54 +02:00
export function IssueDividends(corporation: Corporation, rate: number): void {
if (isNaN(rate) || rate < 0 || rate > corpConstants.dividendMaxRate) {
throw new Error(`Invalid value. Must be an number between 0 and ${corpConstants.dividendMaxRate}`);
2021-09-05 01:09:30 +02:00
}
2021-09-02 04:16:48 +02:00
corporation.dividendRate = rate;
2021-09-02 06:36:33 +02:00
}
export function GoPublic(corporation: Corporation, numShares: number): void {
const ceoOwnership = (corporation.numShares - numShares) / corporation.totalShares;
const initialSharePrice = corporation.getTargetSharePrice(ceoOwnership);
if (isNaN(numShares) || numShares < 0) {
throw new Error("Invalid value for number of issued shares");
}
if (numShares > corporation.numShares) {
throw new Error("You don't have that many shares to issue!");
}
corporation.public = true;
corporation.sharePrice = initialSharePrice;
corporation.issuedShares += numShares;
corporation.numShares -= numShares;
corporation.addNonIncomeFunds(numShares * initialSharePrice);
}
export function IssueNewShares(
corporation: Corporation,
amount: number,
): [profit: number, amount: number, privateShares: number] {
const failureReason = issueNewSharesFailureReason(corporation, amount);
if (failureReason) throw new Error(failureReason);
const ceoOwnership = corporation.numShares / (corporation.totalShares + amount);
const newSharePrice = corporation.getTargetSharePrice(ceoOwnership);
const profit = (amount * (corporation.sharePrice + newSharePrice)) / 2;
const cooldownMultiplier = corporation.totalShares / corpConstants.initialShares;
corporation.issueNewSharesCooldown = corpConstants.issueNewSharesCooldown * cooldownMultiplier;
const privateOwnedRatio = corporation.investorShares / corporation.totalShares;
const maxPrivateShares = Math.round((amount / 2) * privateOwnedRatio);
const privateShares = Math.round(getRandomInt(0, maxPrivateShares) / 10e6) * 10e6;
corporation.issuedShares += amount - privateShares;
corporation.investorShares += privateShares;
corporation.totalShares += amount;
corporation.addNonIncomeFunds(profit);
// Set sharePrice directly because all formulas will be based on stale cycleValuation data
corporation.sharePrice = newSharePrice;
return [profit, amount, privateShares];
}
export function AcceptInvestmentOffer(corporation: Corporation): void {
if (
corporation.fundingRound >= corpConstants.fundingRoundShares.length ||
corporation.fundingRound >= corpConstants.fundingRoundMultiplier.length ||
corporation.public
) {
throw new Error("No more investment offers are available.");
}
const val = corporation.valuation;
const percShares = corpConstants.fundingRoundShares[corporation.fundingRound];
const roundMultiplier = corpConstants.fundingRoundMultiplier[corporation.fundingRound];
const funding = val * percShares * roundMultiplier;
const investShares = Math.floor(corpConstants.initialShares * percShares);
corporation.fundingRound++;
corporation.addNonIncomeFunds(funding);
corporation.numShares -= investShares;
corporation.investorShares += investShares;
}
export function SellMaterial(material: Material, amount: string, price: string): void {
2021-09-05 01:09:30 +02:00
if (price === "") price = "0";
if (amount === "") amount = "0";
2021-09-05 01:09:30 +02:00
let cost = price.replace(/\s+/g, "");
2021-11-16 05:49:33 +01:00
cost = cost.replace(/[^-()\d/*+.MPe]/g, ""); //Sanitize cost
let temp = cost.replace(/MP/, "1.234e5");
2021-09-05 01:09:30 +02:00
try {
if (temp.includes("MP")) throw "Only one reference to MP is allowed in sell price.";
2021-09-05 01:09:30 +02:00
temp = eval(temp);
} catch (e) {
throw new Error("Invalid value or expression for sell price field: " + e);
}
if (temp == null || isNaN(parseFloat(temp))) {
2021-09-05 01:09:30 +02:00
throw new Error("Invalid value or expression for sell price field");
}
if (cost.includes("MP")) {
material.desiredSellPrice = cost; //Dynamically evaluated
2021-09-05 01:09:30 +02:00
} else {
material.desiredSellPrice = temp;
2021-09-05 01:09:30 +02:00
}
//Parse quantity
amount = amount.toUpperCase();
if (amount.includes("MAX") || amount.includes("PROD") || amount.includes("INV")) {
let q = amount.replace(/\s+/g, "");
q = q.replace(/[^-()\d/*+.MAXPRODINV]/g, "");
let tempQty = q.replace(/MAX/g, material.maxSellPerCycle.toString());
tempQty = tempQty.replace(/PROD/g, material.productionAmount.toString());
tempQty = tempQty.replace(/INV/g, material.productionAmount.toString());
2021-09-02 06:36:33 +02:00
try {
2021-09-05 01:09:30 +02:00
tempQty = eval(tempQty);
} catch (e) {
2022-03-16 12:08:00 +01:00
throw new Error("Invalid value or expression for sell quantity field: " + e);
2021-09-02 06:36:33 +02:00
}
if (tempQty == null || isNaN(parseFloat(tempQty))) {
2022-03-16 12:08:00 +01:00
throw new Error("Invalid value or expression for sell quantity field");
2021-09-02 06:36:33 +02:00
}
material.desiredSellAmount = q; //Use sanitized input
} else if (isNaN(parseFloat(amount)) || parseFloat(amount) < 0) {
2022-03-16 12:08:00 +01:00
throw new Error("Invalid value for sell quantity field! Must be numeric or 'PROD' or 'MAX'");
2021-09-05 01:09:30 +02:00
} else {
let q = parseFloat(amount);
2021-09-05 01:09:30 +02:00
if (isNaN(q)) {
q = 0;
}
material.desiredSellAmount = q;
2021-09-05 01:09:30 +02:00
}
}
2021-09-02 06:36:33 +02:00
export function SellProduct(product: Product, city: CityName, amt: string, price: string, all: boolean): void {
2021-09-05 01:09:30 +02:00
//Parse price
// initliaze newPrice with oldPrice as default
let newPrice = product.cityData[city].desiredSellPrice;
2021-09-05 01:09:30 +02:00
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/*+.MPe]/g, "");
let temp = price.replace(/MP/, "1.234e5");
2021-09-05 01:09:30 +02:00
try {
if (temp.includes("MP")) throw "Only one reference to MP is allowed in sell price.";
2021-09-05 01:09:30 +02:00
temp = eval(temp);
} catch (e) {
2022-03-16 12:08:00 +01:00
throw new Error("Invalid value or expression for sell price field: " + e);
2021-09-05 01:09:30 +02:00
}
if (temp == null || isNaN(parseFloat(temp))) {
2022-03-16 12:08:00 +01:00
throw new Error("Invalid value or expression for sell price field.");
2021-09-05 01:09:30 +02:00
}
newPrice = price; //Use sanitized price
2021-09-05 01:09:30 +02:00
} else {
const cost = parseFloat(price);
if (isNaN(cost)) {
throw new Error("Invalid value for sell price field");
}
newPrice = cost;
2021-09-05 01:09:30 +02:00
}
2021-09-02 06:36:33 +02:00
2021-09-05 01:09:30 +02:00
// Parse quantity
2021-10-12 04:54:28 +02:00
amt = amt.toUpperCase();
//initialize newAmount with old as default
let newAmount = product.cityData[city].desiredSellAmount;
if (amt.includes("MAX") || amt.includes("PROD") || amt.includes("INV")) {
2021-09-05 01:09:30 +02:00
//Dynamically evaluated quantity. First test to make sure its valid
let qty = amt.replace(/\s+/g, "");
qty = qty.replace(/[^-()\d/*+.MAXPRODINV]/g, "");
let temp = qty.replace(/MAX/g, product.maxSellAmount.toString());
temp = temp.replace(/PROD/g, product.cityData[city].productionAmount.toString());
temp = temp.replace(/INV/g, product.cityData[city].stored.toString());
2021-09-05 01:09:30 +02:00
try {
temp = eval(temp);
} catch (e) {
2022-03-16 12:08:00 +01:00
throw new Error("Invalid value or expression for sell quantity field: " + e);
2021-09-02 06:36:33 +02:00
}
if (temp == null || isNaN(parseFloat(temp))) {
2022-03-16 12:08:00 +01:00
throw new Error("Invalid value or expression for sell quantity field");
2021-09-05 01:09:30 +02:00
}
newAmount = qty; //Use sanitized input
} else if (isNaN(parseFloat(amt)) || parseFloat(amt) < 0) {
2022-03-16 12:08:00 +01:00
throw new Error("Invalid value for sell quantity field! Must be numeric or 'PROD' or 'MAX'");
2021-09-05 01:09:30 +02:00
} else {
let qty = parseFloat(amt);
if (isNaN(qty)) {
qty = 0;
}
newAmount = qty;
}
//apply new price and amount to all or just current
if (all) {
for (const cityName of Object.values(CityName)) {
product.cityData[cityName].desiredSellAmount = newAmount;
product.cityData[cityName].desiredSellPrice = newPrice;
}
} else {
product.cityData[city].desiredSellAmount = newAmount;
product.cityData[city].desiredSellPrice = newPrice;
2021-09-05 01:09:30 +02:00
}
2021-09-02 06:36:33 +02:00
}
2021-09-09 05:47:34 +02:00
export function SetSmartSupply(warehouse: Warehouse, smartSupply: boolean): void {
2021-09-05 01:09:30 +02:00
warehouse.smartSupplyEnabled = smartSupply;
2021-09-03 22:02:41 +02:00
}
export function SetSmartSupplyOption(warehouse: Warehouse, material: Material, useOption: CorpSmartSupplyOption): void {
warehouse.smartSupplyOptions[material.name] = useOption;
2021-09-10 08:17:55 +02:00
}
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) {
2021-09-09 05:47:34 +02:00
throw new Error(`Invalid amount '${amt}' to buy material '${material.name}'`);
2021-09-05 01:09:30 +02:00
}
material.buyAmount = amt;
2021-09-05 01:09:30 +02:00
}
2021-09-10 06:13:28 +02:00
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) {
throw new Error(`Invalid input amount`);
}
2022-04-02 14:17:30 +02:00
if (amt > maxAmount) {
throw new Error(`You do not have enough warehouse size to fit this purchase`);
}
const cost = amt * material.marketPrice;
if (corp.funds >= cost) {
corp.funds = corp.funds - cost;
material.stored += amt;
warehouse.sizeUsed = warehouse.sizeUsed + amt * matSize;
} else {
throw new Error(`You cannot afford this purchase.`);
}
}
2022-09-20 12:47:54 +02:00
export function SellShares(corporation: Corporation, numShares: number): number {
const failureReason = sellSharesFailureReason(corporation, numShares);
if (failureReason) throw new Error(failureReason);
const [profit, newSharePrice, newSharesUntilUpdate] = corporation.calculateShareSale(numShares);
corporation.numShares -= numShares;
corporation.issuedShares += numShares;
corporation.sharePrice = newSharePrice;
corporation.shareSalesUntilPriceUpdate = newSharesUntilUpdate;
corporation.shareSaleCooldown = corpConstants.sellSharesCooldown;
2022-09-06 15:07:12 +02:00
Player.gainMoney(profit, "corporation");
return profit;
}
2022-09-20 12:47:54 +02:00
export function BuyBackShares(corporation: Corporation, numShares: number): boolean {
const failureReason = buybackSharesFailureReason(corporation, numShares);
if (failureReason) throw new Error(failureReason);
const [cost, newSharePrice, newSharesUntilUpdate] = corporation.calculateShareBuyback(numShares);
corporation.numShares += numShares;
corporation.issuedShares -= numShares;
corporation.sharePrice = newSharePrice;
corporation.shareSalesUntilPriceUpdate = newSharesUntilUpdate;
Player.loseMoney(cost, "corporation");
return true;
}
2022-09-20 12:47:54 +02:00
export function UpgradeOfficeSize(corp: Corporation, office: OfficeSpace, size: number): void {
const initialPriceMult = Math.round(office.size / corpConstants.officeInitialSize);
2021-09-10 06:13:28 +02:00
const costMultiplier = 1.09;
// Calculate cost to upgrade size by 15 employees
let mult = 0;
for (let i = 0; i < size / corpConstants.officeInitialSize; ++i) {
2021-09-10 06:13:28 +02:00
mult += Math.pow(costMultiplier, initialPriceMult + i);
}
const cost = corpConstants.officeInitialCost * mult;
2021-11-12 03:35:26 +01:00
if (corp.funds < cost) return;
2021-09-10 06:13:28 +02:00
office.size += size;
corp.addNonIncomeFunds(-cost);
2021-09-10 06:13:28 +02:00
}
export function BuyTea(corp: Corporation, office: OfficeSpace): boolean {
const cost = office.getTeaCost();
if (corp.funds < cost || !office.setTea()) return false;
corp.funds -= cost;
return true;
}
2022-09-20 12:47:54 +02:00
export function ThrowParty(corp: Corporation, office: OfficeSpace, costPerEmployee: number): number {
const mult = 1 + costPerEmployee / 10e6;
const cost = costPerEmployee * office.numEmployees;
if (corp.funds < cost) {
return 0;
}
if (!office.setParty(mult)) {
return 0;
}
corp.funds -= cost;
2021-09-10 06:13:28 +02:00
return mult;
}
export function purchaseWarehouse(corp: Corporation, division: Division, city: CityName): void {
if (corp.funds < corpConstants.warehouseInitialCost) return;
if (division.warehouses[city]) return;
corp.addNonIncomeFunds(-corpConstants.warehouseInitialCost);
2021-09-10 06:13:28 +02:00
division.warehouses[city] = new Warehouse({
division: division,
2021-09-10 06:13:28 +02:00
loc: city,
size: corpConstants.warehouseInitialSize,
2021-09-10 06:13:28 +02:00
});
}
export function UpgradeWarehouseCost(warehouse: Warehouse, amt: number): number {
return Array.from(Array(amt).keys()).reduce(
(acc, index) => acc + corpConstants.warehouseSizeUpgradeCostBase * Math.pow(1.07, warehouse.level + 1 + index),
0,
);
}
export function UpgradeWarehouse(corp: Corporation, division: Division, warehouse: Warehouse, amt = 1): void {
const sizeUpgradeCost = UpgradeWarehouseCost(warehouse, amt);
if (corp.funds < sizeUpgradeCost) return;
warehouse.level += amt;
warehouse.updateSize(corp, division);
corp.addNonIncomeFunds(-sizeUpgradeCost);
}
export function HireAdVert(corp: Corporation, division: Division): void {
const cost = division.getAdVertCost();
2021-11-12 03:35:26 +01:00
if (corp.funds < cost) return;
corp.funds = corp.funds - cost;
division.applyAdVert(corp);
}
export function MakeProduct(
2022-09-20 12:47:54 +02:00
corp: Corporation,
division: Division,
city: CityName,
productName: string,
designInvest: number,
marketingInvest: number,
): void {
// For invalid investment inputs, just use 0
if (isNaN(designInvest) || designInvest < 0) designInvest = 0;
if (isNaN(marketingInvest) || marketingInvest < 0) marketingInvest = 0;
if (!division.offices[city]) {
throw new Error(`Cannot develop a product in a city without an office!`);
}
if (productName == null || productName === "") {
throw new Error("You must specify a name for your product!");
}
if (!division.makesProducts) {
throw new Error("You cannot create products for this industry!");
}
2021-11-12 03:35:26 +01:00
if (corp.funds < designInvest + marketingInvest) {
throw new Error("You don't have enough company funds to make this large of an investment");
}
if (division.products.size >= division.maxProducts) {
throw new Error(`You are already at the max products (${division.maxProducts}) for division: ${division.name}!`);
}
const product = new Product({
name: productName.replace(/[<>]/g, "").trim(), //Sanitize for HTMl elements?
createCity: city,
designInvestment: designInvest,
advertisingInvestment: marketingInvest,
});
if (division.products.has(product.name)) {
throw new Error(`You already have a product with this name!`);
}
2021-11-12 03:35:26 +01:00
corp.funds = corp.funds - (designInvest + marketingInvest);
division.products.set(product.name, product);
}
2021-09-10 08:17:55 +02:00
2023-05-31 00:47:48 +02:00
export function Research(researchingDivision: Division, researchName: CorpResearchName): void {
const corp = Player.corporation;
if (!corp) return;
2023-05-31 00:47:48 +02:00
const researchTree = IndustryResearchTrees[researchingDivision.type];
if (researchTree === undefined) throw new Error(`No research tree for industry '${researchingDivision.type}'`);
2021-09-10 08:17:55 +02:00
const research = ResearchMap[researchName];
const researchNode = researchTree.findNode(researchName);
const researchPreReq = researchNode?.parent?.researchName;
//Check to see if the research request has any pre-reqs that need to be researched first.
if (researchPreReq) {
if (!researchingDivision.researched?.has(researchPreReq)) {
throw new Error(
`Division ${researchingDivision.name} requires ${researchPreReq} before researching ${research.name}`,
);
}
}
2023-05-31 00:47:48 +02:00
if (researchingDivision.researched.has(researchName)) return;
if (researchingDivision.researchPoints < research.cost) {
2021-09-10 08:17:55 +02:00
throw new Error(`You do not have enough Scientific Research for ${research.name}`);
2023-05-31 00:47:48 +02:00
}
researchingDivision.researchPoints -= research.cost;
2021-09-10 08:17:55 +02:00
// Get the Node from the Research Tree and set its 'researched' property
researchTree.research(researchName);
2023-05-31 00:47:48 +02:00
// All divisions of the same type as the researching division get the new research.
for (const division of corp.divisions.values()) {
if (division.type !== researchingDivision.type) continue;
division.researched.add(researchName);
// Handle researches that need to have their effects manually applied here.
// Warehouse size needs to be updated here because it is not recalculated during normal processing.
if (researchName == "Drones - Transport") {
for (const warehouse of getRecordValues(division.warehouses)) {
warehouse.updateSize(corp, division);
}
2022-09-23 11:54:04 +02:00
}
}
2021-09-10 08:17:55 +02:00
}
/** Set a new export for a material. Throw on any invalid input. */
export function ExportMaterial(
targetDivision: Division,
targetCity: CityName,
material: Material,
amount: string,
): void {
if (!isRelevantMaterial(material.name, targetDivision)) {
throw new Error(`You cannot export material: ${material.name} to division: ${targetDivision.name}!`);
2021-09-10 08:17:55 +02:00
}
if (!targetDivision.warehouses[targetCity]) {
throw new Error(`Cannot export to ${targetCity} in division ${targetDivision.name} because there is no warehouse.`);
}
if (material === targetDivision.warehouses[targetCity]?.materials[material.name]) {
throw new Error(`Source and target division/city cannot be the same.`);
}
for (const existingExport of material.exports) {
if (existingExport.division === targetDivision.name && existingExport.city === targetCity) {
throw new Error(`Tried to initialize an export to a duplicate warehouse.
Target warehouse (division / city): ${existingExport.division} / ${existingExport.city}
Existing export amount: ${existingExport.amount}
Attempted export amount: ${amount}`);
}
2021-09-10 08:17:55 +02:00
}
// Perform sanitization and tests
let sanitizedAmt = amount.replace(/\s+/g, "").toUpperCase();
sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAXEPRODINV]/g, "");
for (const testReplacement of ["(1.23)", "(-1.23)"]) {
const replaced = sanitizedAmt.replace(/(MAX|IPROD|EPROD|IINV|EINV)/g, testReplacement);
let evaluated, error;
try {
evaluated = eval(replaced);
} catch (e) {
error = e;
}
if (!error && isNaN(evaluated)) error = "evaluated value is NaN";
if (error) {
throw new Error(`Error while trying to set the exported amount of ${material.name}.
Error occurred while testing keyword replacement with ${testReplacement}.
Your input: ${amount}
Sanitized input: ${sanitizedAmt}
Input after replacement: ${replaced}
Evaluated value: ${evaluated}
Error encountered: ${error}`);
}
}
const exportObj = { division: targetDivision.name, city: targetCity, amount: sanitizedAmt };
material.exports.push(exportObj);
2021-09-10 08:17:55 +02:00
}
export function CancelExportMaterial(divisionName: string, cityName: CityName, material: Material): void {
const index = material.exports.findIndex((exp) => exp.division === divisionName && exp.city === cityName);
if (index === -1) return;
material.exports.splice(index, 1);
2021-09-10 08:17:55 +02:00
}
export function LimitProductProduction(product: Product, cityName: CityName, quantity: number): void {
if (quantity < 0 || isNaN(quantity)) {
product.cityData[cityName].productionLimit = null;
2021-09-10 08:17:55 +02:00
} else {
product.cityData[cityName].productionLimit = quantity;
2021-09-10 08:17:55 +02:00
}
}
export function LimitMaterialProduction(material: Material, quantity: number): void {
if (quantity < 0 || isNaN(quantity)) {
material.productionLimit = null;
} else {
material.productionLimit = quantity;
}
}
2021-09-10 08:17:55 +02:00
export function SetMaterialMarketTA1(material: Material, on: boolean): void {
material.marketTa1 = on;
}
export function SetMaterialMarketTA2(material: Material, on: boolean): void {
material.marketTa2 = on;
}
export function SetProductMarketTA1(product: Product, on: boolean): void {
product.marketTa1 = on;
}
export function SetProductMarketTA2(product: Product, on: boolean): void {
product.marketTa2 = on;
}