From 8b55b0293c33e28d40ad573efdf4c282e74d5a95 Mon Sep 17 00:00:00 2001 From: phyzical Date: Fri, 11 Feb 2022 23:48:19 +0800 Subject: [PATCH 1/4] corp api fixes * added early out in UpgradeWarehouse when not enough cash * added checks to enforce maxProducts * added checks to enforce export material is valid for location * added checks for market ti reasearches --- src/Corporation/Actions.ts | 25 +++++++++++++++++++++---- src/Corporation/ui/ExportModal.tsx | 3 ++- src/NetscriptFunctions/Corporation.ts | 18 +++++++++++++----- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts index e69d5bd72..752fe1b5c 100644 --- a/src/Corporation/Actions.ts +++ b/src/Corporation/Actions.ts @@ -19,7 +19,6 @@ export function NewIndustry(corporation: ICorporation, industry: string, name: s for (let i = 0; i < corporation.divisions.length; ++i) { if (corporation.divisions[i].name === name) { throw new Error("This division name is already in use!"); - return; } } @@ -290,6 +289,7 @@ export function PurchaseWarehouse(corp: ICorporation, division: IIndustry, city: export function UpgradeWarehouse(corp: ICorporation, division: IIndustry, warehouse: Warehouse): void { const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1); + if (corp.funds < sizeUpgradeCost) return; ++warehouse.level; warehouse.updateSize(corp, division); corp.funds = corp.funds - sizeUpgradeCost; @@ -343,17 +343,29 @@ export function MakeProduct( if (corp.funds < designInvest + marketingInvest) { throw new Error("You don't have enough company funds to make this large of an investment"); } + let maxProducts = 3 + if (division.hasResearch("uPgrade: Capacity.II")) { + maxProducts = 5 + } else if (division.hasResearch("uPgrade: Capacity.I")) { + maxProducts = 4 + } + const products = division.products + if (Object.keys(products).length >= maxProducts) { + throw new Error(`You are already at the max products (${maxProducts}) for division: ${division.name}!`); + } + const product = new Product({ name: productName.replace(/[<>]/g, ""), //Sanitize for HTMl elements createCity: city, designCost: designInvest, advCost: marketingInvest, }); - if (division.products[product.name] instanceof Product) { + if (products[product.name] instanceof Product) { throw new Error(`You already have a product with this name!`); } + corp.funds = corp.funds - (designInvest + marketingInvest); - division.products[product.name] = product; + products[product.name] = product; } export function Research(division: IIndustry, researchName: string): void { @@ -372,7 +384,7 @@ export function Research(division: IIndustry, researchName: string): void { division.researched[researchName] = true; } -export function ExportMaterial(divisionName: string, cityName: string, material: Material, amt: string): void { +export function ExportMaterial(divisionName: string, cityName: string, material: Material, amt: string, warehouse?: Warehouse | 0): void { // Sanitize amt let sanitizedAmt = amt.replace(/\s+/g, "").toUpperCase(); sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAX]/g, ""); @@ -388,6 +400,11 @@ export function ExportMaterial(divisionName: string, cityName: string, material: if (n == null || isNaN(n) || n < 0) { throw new Error("Invalid amount entered for export"); } + + if (!warehouse || !warehouse.materials.hasOwnProperty(material.name)) { + throw new Error(`You cannot export material: ${material.name} to division: ${divisionName}!`); + } + const exportObj = { ind: divisionName, city: cityName, amt: sanitizedAmt }; material.exp.push(exportObj); } diff --git a/src/Corporation/ui/ExportModal.tsx b/src/Corporation/ui/ExportModal.tsx index 54460583c..445d50e2b 100644 --- a/src/Corporation/ui/ExportModal.tsx +++ b/src/Corporation/ui/ExportModal.tsx @@ -50,7 +50,8 @@ export function ExportModal(props: IProps): React.ReactElement { function exportMaterial(): void { try { - ExportMaterial(industry, city, props.mat, amt); + const warehouse = currentDivision && currentDivision.warehouses.first + ExportMaterial(industry, city, props.mat, amt, warehouse); } catch (err) { dialogBoxCreate(err + ""); } diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts index c961b1482..541cd0ce4 100644 --- a/src/NetscriptFunctions/Corporation.ts +++ b/src/NetscriptFunctions/Corporation.ts @@ -132,11 +132,11 @@ export function NetscriptCorporation( function getInvestmentOffer(): InvestmentOffer { const corporation = getCorporation(); - if (corporation.fundingRound >= CorporationConstants.FundingRoundShares.length || corporation.fundingRound >= CorporationConstants.FundingRoundMultiplier.length || corporation.public) + if (corporation.fundingRound >= CorporationConstants.FundingRoundShares.length || corporation.fundingRound >= CorporationConstants.FundingRoundMultiplier.length || corporation.public) return { funds: 0, shares: 0, - round: corporation.fundingRound + 1 // Make more readable + round: corporation.fundingRound + 1 // Make more readable }; // Don't throw an error here, no reason to have a second function to check if you can get investment. const val = corporation.determineValuation(); const percShares = CorporationConstants.FundingRoundShares[corporation.fundingRound]; @@ -146,7 +146,7 @@ export function NetscriptCorporation( return { funds: funding, shares: investShares, - round: corporation.fundingRound + 1 // Make more readable + round: corporation.fundingRound + 1 // Make more readable }; } @@ -462,7 +462,7 @@ export function NetscriptCorporation( const targetCity = helper.string("exportMaterial", "targetCity", atargetCity); const materialName = helper.string("exportMaterial", "materialName", amaterialName); const amt = helper.string("exportMaterial", "amt", aamt); - ExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + ""); + ExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + "", getWarehouse(targetDivision,targetCity)); }, cancelExportMaterial: function ( asourceDivision: any, @@ -487,7 +487,9 @@ export function NetscriptCorporation( const cityName = helper.string("setMaterialMarketTA1", "cityName", acityName); const materialName = helper.string("setMaterialMarketTA1", "materialName", amaterialName); const on = helper.boolean(aon); - SetMaterialMarketTA1(getMaterial(divisionName, cityName, materialName), on); + if (!getDivision(divisionName).hasResearch("Market-TA.I")) + throw helper.makeRuntimeErrorMsg(`corporation.setMaterialMarketTA1`, `You have not researched MarketTA.I for division: ${divisionName}`); + SetMaterialMarketTA1( getMaterial(divisionName, cityName, materialName), on); }, setMaterialMarketTA2: function (adivisionName: any, acityName: any, amaterialName: any, aon: any): void { checkAccess("setMaterialMarketTA2", 7); @@ -495,6 +497,8 @@ export function NetscriptCorporation( const cityName = helper.string("setMaterialMarketTA2", "cityName", acityName); const materialName = helper.string("setMaterialMarketTA2", "materialName", amaterialName); const on = helper.boolean(aon); + if (!getDivision(divisionName).hasResearch("Market-TA.II")) + throw helper.makeRuntimeErrorMsg(`corporation.setMaterialMarketTA2`, `You have not researched MarketTA.II for division: ${divisionName}`); SetMaterialMarketTA2(getMaterial(divisionName, cityName, materialName), on); }, setProductMarketTA1: function (adivisionName: any, aproductName: any, aon: any): void { @@ -502,6 +506,8 @@ export function NetscriptCorporation( const divisionName = helper.string("setProductMarketTA1", "divisionName", adivisionName); const productName = helper.string("setProductMarketTA1", "productName", aproductName); const on = helper.boolean(aon); + if (!getDivision(divisionName).hasResearch("Market-TA.I")) + throw helper.makeRuntimeErrorMsg(`corporation.setProductMarketTA1`, `You have not researched MarketTA.I for division: ${divisionName}`); SetProductMarketTA1(getProduct(divisionName, productName), on); }, setProductMarketTA2: function (adivisionName: any, aproductName: any, aon: any): void { @@ -509,6 +515,8 @@ export function NetscriptCorporation( const divisionName = helper.string("setProductMarketTA2", "divisionName", adivisionName); const productName = helper.string("setProductMarketTA2", "productName", aproductName); const on = helper.boolean(aon); + if (!getDivision(divisionName).hasResearch("Market-TA.II")) + throw helper.makeRuntimeErrorMsg(`corporation.setMaterialMarketTA2`, `You have not researched MarketTA.II for division: ${divisionName}`); SetProductMarketTA2(getProduct(divisionName, productName), on); }, }; From 51d10290d2419be294f367edd43f795d54a1d0ae Mon Sep 17 00:00:00 2001 From: phyzical Date: Sat, 12 Feb 2022 00:13:56 +0800 Subject: [PATCH 2/4] added early out for redundant assignment --- src/Corporation/OfficeSpace.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Corporation/OfficeSpace.ts b/src/Corporation/OfficeSpace.ts index 9bd3020f1..dd90304d6 100644 --- a/src/Corporation/OfficeSpace.ts +++ b/src/Corporation/OfficeSpace.ts @@ -184,6 +184,7 @@ export class OfficeSpace { } } + if (jobCount == amount) return false; if ((jobCount + unassignedCount) < amount) return false; for (let i = 0; i < this.employees.length; ++i) { From 3e36e6a80bf963076565172db31dc18e5c9b3cf1 Mon Sep 17 00:00:00 2001 From: phyzical Date: Sat, 12 Feb 2022 11:31:50 +0800 Subject: [PATCH 3/4] few more adjustments * fixed the export material fix * changed teh dev menu fund adjuster for more granular control * added checks for smartSupply and setSmartSupply * few random autolints --- src/Corporation/Actions.ts | 5 ++- src/Corporation/ui/ExportModal.tsx | 3 +- src/DevMenu/ui/Corporation.tsx | 23 ++++++++-- src/NetscriptFunctions/Corporation.ts | 62 +++++++++++++++------------ 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts index 752fe1b5c..c7ef42687 100644 --- a/src/Corporation/Actions.ts +++ b/src/Corporation/Actions.ts @@ -14,6 +14,7 @@ import { EmployeePositions } from "./EmployeePositions"; import { Employee } from "./Employee"; import { IndustryUpgrades } from "./IndustryUpgrades"; import { ResearchMap } from "./ResearchMap"; +import { isRelevantMaterial } from "./ui/Helpers"; export function NewIndustry(corporation: ICorporation, industry: string, name: string): void { for (let i = 0; i < corporation.divisions.length; ++i) { @@ -384,7 +385,7 @@ export function Research(division: IIndustry, researchName: string): void { division.researched[researchName] = true; } -export function ExportMaterial(divisionName: string, cityName: string, material: Material, amt: string, warehouse?: Warehouse | 0): void { +export function ExportMaterial(divisionName: string, cityName: string, material: Material, amt: string, division?: Industry): void { // Sanitize amt let sanitizedAmt = amt.replace(/\s+/g, "").toUpperCase(); sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAX]/g, ""); @@ -401,7 +402,7 @@ export function ExportMaterial(divisionName: string, cityName: string, material: throw new Error("Invalid amount entered for export"); } - if (!warehouse || !warehouse.materials.hasOwnProperty(material.name)) { + if (!division || !isRelevantMaterial(material.name, division)) { throw new Error(`You cannot export material: ${material.name} to division: ${divisionName}!`); } diff --git a/src/Corporation/ui/ExportModal.tsx b/src/Corporation/ui/ExportModal.tsx index 445d50e2b..ab39e3e44 100644 --- a/src/Corporation/ui/ExportModal.tsx +++ b/src/Corporation/ui/ExportModal.tsx @@ -50,8 +50,7 @@ export function ExportModal(props: IProps): React.ReactElement { function exportMaterial(): void { try { - const warehouse = currentDivision && currentDivision.warehouses.first - ExportMaterial(industry, city, props.mat, amt, warehouse); + ExportMaterial(industry, city, props.mat, amt, currentDivision); } catch (err) { dialogBoxCreate(err + ""); } diff --git a/src/DevMenu/ui/Corporation.tsx b/src/DevMenu/ui/Corporation.tsx index d279dd055..ce4250a91 100644 --- a/src/DevMenu/ui/Corporation.tsx +++ b/src/DevMenu/ui/Corporation.tsx @@ -19,10 +19,18 @@ interface IProps { export function Corporation(props: IProps): React.ReactElement { function addTonsCorporationFunds(): void { if (props.player.corporation) { - props.player.corporation.funds = props.player.corporation.funds + 1e99; + props.player.corporation.funds = props.player.corporation.funds + bigNumber; } } + function modifyCorporationFunds(modify: number): (x: number) => void { + return function (funds: number): void { + if (props.player.corporation) { + props.player.corporation.funds += funds * modify; + } + }; + } + function resetCorporationFunds(): void { if (props.player.corporation) { props.player.corporation.funds = props.player.corporation.funds - props.player.corporation.funds; @@ -77,8 +85,17 @@ export function Corporation(props: IProps): React.ReactElement { - - + Funds: + + + diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts index 541cd0ce4..d5d63088f 100644 --- a/src/NetscriptFunctions/Corporation.ts +++ b/src/NetscriptFunctions/Corporation.ts @@ -193,7 +193,7 @@ export function NetscriptCorporation( function bribe(factionName: string, amountCash: number, amountShares: number): boolean { if (!player.factions.includes(factionName)) throw new Error("Invalid faction name"); - if (isNaN(amountCash) || amountCash < 0 || isNaN(amountShares) || amountShares < 0) throw new Error("Invalid value for amount field! Must be numeric, grater than 0."); + if (isNaN(amountCash) || amountCash < 0 || isNaN(amountShares) || amountShares < 0) throw new Error("Invalid value for amount field! Must be numeric, grater than 0."); const corporation = getCorporation(); if (corporation.funds < amountCash) return false; if (corporation.numShares < amountShares) return false; @@ -271,25 +271,25 @@ export function NetscriptCorporation( function getSafeDivision(division: Industry): NSDivision { const cities: string[] = []; - for (const office of Object.values(division.offices)) { - if (office === 0) continue; - cities.push(office.loc); - } - return { - name: division.name, - type: division.type, - awareness: division.awareness, - popularity: division.popularity, - prodMult: division.prodMult, - research: division.sciResearch.qty, - lastCycleRevenue: division.lastCycleRevenue, - lastCycleExpenses: division.lastCycleExpenses, - thisCycleRevenue: division.thisCycleRevenue, - thisCycleExpenses: division.thisCycleExpenses, - upgrades: division.upgrades, - cities: cities, - products: division.products === undefined ? [] : Object.keys(division.products), - }; + for (const office of Object.values(division.offices)) { + if (office === 0) continue; + cities.push(office.loc); + } + return { + name: division.name, + type: division.type, + awareness: division.awareness, + popularity: division.popularity, + prodMult: division.prodMult, + research: division.sciResearch.qty, + lastCycleRevenue: division.lastCycleRevenue, + lastCycleExpenses: division.lastCycleExpenses, + thisCycleRevenue: division.thisCycleRevenue, + thisCycleExpenses: division.thisCycleExpenses, + upgrades: division.upgrades, + cities: cities, + products: division.products === undefined ? [] : Object.keys(division.products), + }; } const warehouseAPI: WarehouseAPI = { @@ -409,6 +409,8 @@ export function NetscriptCorporation( const cityName = helper.string("sellProduct", "cityName", acityName); const enabled = helper.boolean(aenabled); const warehouse = getWarehouse(divisionName, cityName); + if (!hasUnlockUpgrade("SmartSupply")) + throw helper.makeRuntimeErrorMsg(`corporation.setSmartSupply`, `You have not purchased the SmartSupply upgrade!`); SetSmartSupply(warehouse, enabled); }, setSmartSupplyUseLeftovers: function (adivisionName: any, acityName: any, amaterialName: any, aenabled: any): void { @@ -419,6 +421,8 @@ export function NetscriptCorporation( const enabled = helper.boolean(aenabled); const warehouse = getWarehouse(divisionName, cityName); const material = getMaterial(divisionName, cityName, materialName); + if (!hasUnlockUpgrade("SmartSupply")) + throw helper.makeRuntimeErrorMsg(`corporation.setSmartSupply`, `You have not purchased the SmartSupply upgrade!`); SetSmartSupplyUseLeftovers(warehouse, material, enabled); }, buyMaterial: function (adivisionName: any, acityName: any, amaterialName: any, aamt: any): void { @@ -462,7 +466,7 @@ export function NetscriptCorporation( const targetCity = helper.string("exportMaterial", "targetCity", atargetCity); const materialName = helper.string("exportMaterial", "materialName", amaterialName); const amt = helper.string("exportMaterial", "amt", aamt); - ExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + "", getWarehouse(targetDivision,targetCity)); + ExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + "", getDivision(targetDivision)); }, cancelExportMaterial: function ( asourceDivision: any, @@ -489,7 +493,7 @@ export function NetscriptCorporation( const on = helper.boolean(aon); if (!getDivision(divisionName).hasResearch("Market-TA.I")) throw helper.makeRuntimeErrorMsg(`corporation.setMaterialMarketTA1`, `You have not researched MarketTA.I for division: ${divisionName}`); - SetMaterialMarketTA1( getMaterial(divisionName, cityName, materialName), on); + SetMaterialMarketTA1(getMaterial(divisionName, cityName, materialName), on); }, setMaterialMarketTA2: function (adivisionName: any, acityName: any, amaterialName: any, aon: any): void { checkAccess("setMaterialMarketTA2", 7); @@ -516,7 +520,7 @@ export function NetscriptCorporation( const productName = helper.string("setProductMarketTA2", "productName", aproductName); const on = helper.boolean(aon); if (!getDivision(divisionName).hasResearch("Market-TA.II")) - throw helper.makeRuntimeErrorMsg(`corporation.setMaterialMarketTA2`, `You have not researched MarketTA.II for division: ${divisionName}`); + throw helper.makeRuntimeErrorMsg(`corporation.setProductMarketTA2`, `You have not researched MarketTA.II for division: ${divisionName}`); SetProductMarketTA2(getProduct(divisionName, productName), on); }, }; @@ -731,6 +735,8 @@ export function NetscriptCorporation( const percent = helper.number("issueDividends", "percent", apercent); if (percent < 0 || percent > 100) throw new Error("Invalid value for percent field! Must be numeric, grater than 0, and less than 100"); const corporation = getCorporation(); + if (!corporation.public) + throw helper.makeRuntimeErrorMsg(`corporation.issueDividends`, `Your company has not gone public!`); IssueDividends(corporation, percent); }, @@ -789,24 +795,24 @@ export function NetscriptCorporation( const industryName = helper.string("getExpandIndustryCost", "industryName", aindustryName); return getExpandIndustryCost(industryName); }, - getExpandCityCost: function(): number { + getExpandCityCost: function (): number { checkAccess("getExpandCityCost"); return getExpandCityCost(); }, - getInvestmentOffer: function(): InvestmentOffer { + getInvestmentOffer: function (): InvestmentOffer { checkAccess("getInvestmentOffer"); return getInvestmentOffer(); }, - acceptInvestmentOffer: function(): boolean { + acceptInvestmentOffer: function (): boolean { checkAccess("acceptInvestmentOffer"); return acceptInvestmentOffer(); }, - goPublic: function(anumShares: any): boolean { + goPublic: function (anumShares: any): boolean { checkAccess("acceptInvestmentOffer"); const numShares = helper.number("goPublic", "numShares", anumShares); return goPublic(numShares); }, - bribe: function(afactionName: string, aamountCash: any, aamountShares: any): boolean { + bribe: function (afactionName: string, aamountCash: any, aamountShares: any): boolean { checkAccess("bribe"); const factionName = helper.string("bribe", "factionName", afactionName); const amountCash = helper.number("bribe", "amountCash", aamountCash); From da955a4774e83e541e147cb3b9ee0bf56da1ed17 Mon Sep 17 00:00:00 2001 From: phyzical Date: Sun, 13 Feb 2022 17:57:13 +0800 Subject: [PATCH 4/4] refector of setEmployeeToJob --- src/Corporation/OfficeSpace.ts | 36 +++++++++------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/src/Corporation/OfficeSpace.ts b/src/Corporation/OfficeSpace.ts index dd90304d6..1572ee945 100644 --- a/src/Corporation/OfficeSpace.ts +++ b/src/Corporation/OfficeSpace.ts @@ -174,34 +174,16 @@ export class OfficeSpace { } setEmployeeToJob(job: string, amount: number): boolean { - let unassignedCount = 0; - let jobCount = 0; - for (let i = 0; i < this.employees.length; ++i) { - if (this.employees[i].pos === EmployeePositions.Unassigned) { - unassignedCount++; - } else if (this.employees[i].pos === job) { + let jobCount = this.employees.reduce((acc, employee) => (employee.pos === job ? acc + 1 : acc), 0); + + for (const employee of this.employees) { + if (jobCount == amount) return true + if (employee.pos === EmployeePositions.Unassigned && jobCount <= amount) { + employee.pos = job; jobCount++; - } - } - - if (jobCount == amount) return false; - if ((jobCount + unassignedCount) < amount) return false; - - for (let i = 0; i < this.employees.length; ++i) { - if (this.employees[i].pos === EmployeePositions.Unassigned) { - if (jobCount <= amount) { - this.employees[i].pos = job; - jobCount++; - unassignedCount--; - } - if (jobCount === amount) break; - } else if (this.employees[i].pos === job) { - if (jobCount >= amount) { - this.employees[i].pos = EmployeePositions.Unassigned; - jobCount--; - unassignedCount++; - } - if (jobCount === amount) break; + } else if (employee.pos === job && jobCount >= amount) { + employee.pos = EmployeePositions.Unassigned; + jobCount--; } } if (jobCount !== amount) return false;