diff --git a/dist/bitburner.d.ts b/dist/bitburner.d.ts index dc45e4693..56fc6734c 100644 --- a/dist/bitburner.d.ts +++ b/dist/bitburner.d.ts @@ -958,6 +958,16 @@ export declare interface Corporation extends WarehouseAPI, OfficeAPI { * @param percent - Percent of profit to issue as dividends. */ issueDividends(percent: number): void; + /** + * Buyback Shares + * @param amt - Number of shares to attempt to buyback. + */ + buyBackShares(amt: number): void; + /** + * Sell Shares + * @param amt - Number of shares to attempt to sell. + */ + sellShares(amt: number): void; } /** @@ -6635,6 +6645,14 @@ export declare interface WarehouseAPI { * @param amt - Amount of material to buy */ buyMaterial(divisionName: string, cityName: string, materialName: string, amt: number): void; + /** + * Set material to bulk buy + * @param divisionName - Name of the division + * @param cityName - Name of the city + * @param materialName - Name of the material + * @param amt - Amount of material to buy + */ + bulkPurchase(divisionName: string, cityName: string, materialName: string, amt: number): void; /** * Get warehouse data * @param divisionName - Name of the division diff --git a/markdown/bitburner.corporation.buybackshares.md b/markdown/bitburner.corporation.buybackshares.md new file mode 100644 index 000000000..ccc0a787b --- /dev/null +++ b/markdown/bitburner.corporation.buybackshares.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [bitburner](./bitburner.md) > [Corporation](./bitburner.corporation.md) > [buyBackShares](./bitburner.corporation.buybackshares.md) + +## Corporation.buyBackShares() method + +Buyback Shares + +Signature: + +```typescript +buyBackShares(amount: number): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| amount | number | Amount of shares to buy back. | + +Returns: + +void + diff --git a/markdown/bitburner.corporation.md b/markdown/bitburner.corporation.md index 68d553b6f..d41b84ba2 100644 --- a/markdown/bitburner.corporation.md +++ b/markdown/bitburner.corporation.md @@ -19,6 +19,7 @@ export interface Corporation extends WarehouseAPI, OfficeAPI | --- | --- | | [acceptInvestmentOffer()](./bitburner.corporation.acceptinvestmentoffer.md) | Accept investment based on you companies current valuation | | [bribe(factionName, amountCash, amountShares)](./bitburner.corporation.bribe.md) | Bribe a faction | +| [buyBackShares(amount)](./bitburner.corporation.buybackshares.md) | Buyback Shares | | [createCorporation(corporationName, selfFund)](./bitburner.corporation.createcorporation.md) | Create a Corporation | | [expandCity(divisionName, cityName)](./bitburner.corporation.expandcity.md) | Expand to a new city | | [expandIndustry(industryType, divisionName)](./bitburner.corporation.expandindustry.md) | Expand to a new industry | @@ -34,5 +35,6 @@ export interface Corporation extends WarehouseAPI, OfficeAPI | [hasUnlockUpgrade(upgradeName)](./bitburner.corporation.hasunlockupgrade.md) | Check if you have a one time unlockable upgrade | | [issueDividends(percent)](./bitburner.corporation.issuedividends.md) | Issue dividends | | [levelUpgrade(upgradeName)](./bitburner.corporation.levelupgrade.md) | Level an upgrade. | +| [sellShares(amount)](./bitburner.corporation.sellshares.md) | Sell Shares | | [unlockUpgrade(upgradeName)](./bitburner.corporation.unlockupgrade.md) | Unlock an upgrade | diff --git a/markdown/bitburner.corporation.sellshares.md b/markdown/bitburner.corporation.sellshares.md new file mode 100644 index 000000000..6ee2b480a --- /dev/null +++ b/markdown/bitburner.corporation.sellshares.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [bitburner](./bitburner.md) > [Corporation](./bitburner.corporation.md) > [sellShares](./bitburner.corporation.sellshares.md) + +## Corporation.sellShares() method + +Sell Shares + +Signature: + +```typescript +sellShares(amount: number): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| amount | number | Amount of shares to sell. | + +Returns: + +void + diff --git a/markdown/bitburner.warehouseapi.bulkpurchase.md b/markdown/bitburner.warehouseapi.bulkpurchase.md new file mode 100644 index 000000000..3f55a3cac --- /dev/null +++ b/markdown/bitburner.warehouseapi.bulkpurchase.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [bitburner](./bitburner.md) > [WarehouseAPI](./bitburner.warehouseapi.md) > [bulkPurchase](./bitburner.warehouseapi.bulkpurchase.md) + +## WarehouseAPI.bulkPurchase() method + +Set material to bulk buy + +Signature: + +```typescript +bulkPurchase(divisionName: string, cityName: string, materialName: string, amt: number): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| divisionName | string | Name of the division | +| cityName | string | Name of the city | +| materialName | string | Name of the material | +| amt | number | Amount of material to buy | + +Returns: + +void + diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts index c7ef42687..1b648e2b0 100644 --- a/src/Corporation/Actions.ts +++ b/src/Corporation/Actions.ts @@ -1,3 +1,5 @@ +import { IPlayer } from 'src/PersonObjects/IPlayer'; +import { MaterialSizes } from './MaterialSizes'; import { ICorporation } from "./ICorporation"; import { IIndustry } from "./IIndustry"; import { IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData"; @@ -245,6 +247,57 @@ export function BuyMaterial(material: Material, amt: number): void { material.buy = amt; } +export function BulkPurchase(corp: ICorporation, warehouse: Warehouse, material: Material, amt: number): void { + const matSize = MaterialSizes[material.name]; + const maxAmount = (warehouse.size - warehouse.sizeUsed) / matSize; + if (isNaN(amt) || amt < 0) { + throw new Error(`Invalid input amount`); + } + if (amt * matSize > maxAmount) { + throw new Error(`You do not have enough warehouse size to fit this purchase`); + } + const cost = amt * material.bCost; + if (corp.funds >= cost) { + corp.funds = corp.funds - cost; + material.qty += amt; + } else { + throw new Error(`You cannot afford this purchase.`); + } +} + +export function SellShares(corporation: ICorporation, player: IPlayer, numShares: number): number { + if (isNaN(numShares)) throw new Error("Invalid value for number of shares"); + if (numShares < 0) throw new Error("Invalid value for number of shares"); + if (numShares > corporation.numShares) throw new Error("You don't have that many shares to sell!"); + if (!corporation.public) throw new Error("You haven't gone public!"); + if (corporation.shareSaleCooldown) throw new Error("Share sale on cooldown!"); + const stockSaleResults = corporation.calculateShareSale(numShares); + const profit = stockSaleResults[0]; + const newSharePrice = stockSaleResults[1]; + const newSharesUntilUpdate = stockSaleResults[2]; + + corporation.numShares -= numShares; + corporation.issuedShares += numShares; + corporation.sharePrice = newSharePrice; + corporation.shareSalesUntilPriceUpdate = newSharesUntilUpdate; + corporation.shareSaleCooldown = CorporationConstants.SellSharesCooldown; + player.gainMoney(profit, "corporation"); + return profit; +} + +export function BuyBackShares(corporation: ICorporation, player: IPlayer, numShares: number): boolean { + if (isNaN(numShares)) throw new Error("Invalid value for number of shares"); + if (numShares < 0) throw new Error("Invalid value for number of shares"); + if (numShares > corporation.issuedShares) throw new Error("You don't have that many shares to buy!"); + if (!corporation.public) throw new Error("You haven't gone public!"); + const buybackPrice = corporation.sharePrice * 1.1; + if (corporation.funds < (numShares * buybackPrice)) throw new Error("You cant afford that many shares!"); + corporation.numShares += numShares; + corporation.issuedShares -= numShares; + player.loseMoney(numShares * buybackPrice, "corporation"); + return true; +} + export function AssignJob(employee: Employee, job: string): void { if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`); employee.pos = job; @@ -335,6 +388,9 @@ export function MakeProduct( 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!"); + } if (isNaN(designInvest)) { throw new Error("Invalid value for design investment"); } diff --git a/src/Corporation/ui/BuybackSharesModal.tsx b/src/Corporation/ui/BuybackSharesModal.tsx index d58b01842..4d6568af7 100644 --- a/src/Corporation/ui/BuybackSharesModal.tsx +++ b/src/Corporation/ui/BuybackSharesModal.tsx @@ -6,6 +6,8 @@ import { useCorporation } from "./Context"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; +import { BuyBackShares } from '../Actions'; +import { dialogBoxCreate } from '../../ui/React/DialogBox'; interface IProps { open: boolean; @@ -36,20 +38,12 @@ export function BuybackSharesModal(props: IProps): React.ReactElement { function buy(): void { if (disabled) return; - if (shares === null) return; - corp.numShares += shares; - if (isNaN(corp.issuedShares)) { - console.warn("Corporation issuedShares is NaN: " + corp.issuedShares); - console.warn("Converting to number now"); - const res = corp.issuedShares; - if (isNaN(res)) { - corp.issuedShares = 0; - } else { - corp.issuedShares = res; - } + try { + BuyBackShares(corp, player, shares) + } + catch (err) { + dialogBoxCreate(err + ""); } - corp.issuedShares -= shares; - player.loseMoney(shares * buybackPrice, "corporation"); props.onClose(); props.rerender(); } diff --git a/src/Corporation/ui/PurchaseMaterialModal.tsx b/src/Corporation/ui/PurchaseMaterialModal.tsx index 114e53940..5c6f6bda8 100644 --- a/src/Corporation/ui/PurchaseMaterialModal.tsx +++ b/src/Corporation/ui/PurchaseMaterialModal.tsx @@ -4,7 +4,7 @@ import { MaterialSizes } from "../MaterialSizes"; import { Warehouse } from "../Warehouse"; import { Material } from "../Material"; import { numeralWrapper } from "../../ui/numeralFormat"; -import { BuyMaterial } from "../Actions"; +import { BulkPurchase, BuyMaterial } from "../Actions"; import { Modal } from "../../ui/React/Modal"; import { useCorporation, useDivision } from "./Context"; import Typography from "@mui/material/Typography"; @@ -54,33 +54,17 @@ interface IBPProps { warehouse: Warehouse; } -function BulkPurchase(props: IBPProps): React.ReactElement { +function BulkPurchaseSection(props: IBPProps): React.ReactElement { const corp = useCorporation(); const [buyAmt, setBuyAmt] = useState(""); function bulkPurchase(): void { - const amount = parseFloat(buyAmt); - - const matSize = MaterialSizes[props.mat.name]; - const maxAmount = (props.warehouse.size - props.warehouse.sizeUsed) / matSize; - if (amount * matSize > maxAmount) { - dialogBoxCreate(`You do not have enough warehouse size to fit this purchase`); - return; - } - - if (isNaN(amount) || amount < 0) { - dialogBoxCreate("Invalid input amount"); - } else { - const cost = amount * props.mat.bCost; - if (corp.funds >= cost) { - corp.funds = corp.funds - cost; - props.mat.qty += amount; - } else { - dialogBoxCreate(`You cannot afford this purchase.`); - return; - } - props.onClose(); + try { + BulkPurchase(corp, props.warehouse, props.mat, parseFloat(buyAmt)); + } catch (err) { + dialogBoxCreate(err + ""); } + props.onClose(); } function onKeyDown(event: React.KeyboardEvent): void { @@ -164,7 +148,7 @@ export function PurchaseMaterialModal(props: IProps): React.ReactElement { {division.hasResearch("Bulk Purchasing") && ( - + )} diff --git a/src/Corporation/ui/SellSharesModal.tsx b/src/Corporation/ui/SellSharesModal.tsx index f17f564ea..e96fa9959 100644 --- a/src/Corporation/ui/SellSharesModal.tsx +++ b/src/Corporation/ui/SellSharesModal.tsx @@ -4,12 +4,12 @@ import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { Modal } from "../../ui/React/Modal"; import { use } from "../../ui/Context"; import { useCorporation } from "./Context"; -import { CorporationConstants } from "../data/Constants"; import { ICorporation } from "../ICorporation"; import Typography from "@mui/material/Typography"; import TextField from "@mui/material/TextField"; import Button from "@mui/material/Button"; import { Money } from "../../ui/React/Money"; +import { SellShares } from "../Actions"; interface IProps { open: boolean; onClose: () => void; @@ -48,38 +48,23 @@ export function SellSharesModal(props: IProps): React.ReactElement { } function sell(): void { - if (shares === null) return; if (disabled) return; - const stockSaleResults = corp.calculateShareSale(shares); - const profit = stockSaleResults[0]; - const newSharePrice = stockSaleResults[1]; - const newSharesUntilUpdate = stockSaleResults[2]; + try { + const profit = SellShares(corp, player, shares) + props.onClose(); + dialogBoxCreate( + <> + Sold {numeralWrapper.formatMoney(shares)} shares for + . The corporation's stock price fell to  + as a result of dilution. + , + ); - corp.numShares -= shares; - if (isNaN(corp.issuedShares)) { - console.error(`Corporation issuedShares is NaN: ${corp.issuedShares}`); - const res = corp.issuedShares; - if (isNaN(res)) { - corp.issuedShares = 0; - } else { - corp.issuedShares = res; - } + props.rerender(); + } catch (err) { + dialogBoxCreate(err + ""); } - corp.issuedShares += shares; - corp.sharePrice = newSharePrice; - corp.shareSalesUntilPriceUpdate = newSharesUntilUpdate; - corp.shareSaleCooldown = CorporationConstants.SellSharesCooldown; - player.gainMoney(profit, "corporation"); - props.onClose(); - dialogBoxCreate( - <> - Sold {numeralWrapper.formatMoney(shares)} shares for - . The corporation's stock price fell to  - as a result of dilution. - , - ); - props.rerender(); } function onKeyDown(event: React.KeyboardEvent): void { diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts index 8ca4a4d48..365cd2aec 100644 --- a/src/NetscriptFunctions/Corporation.ts +++ b/src/NetscriptFunctions/Corporation.ts @@ -49,6 +49,9 @@ import { SetMaterialMarketTA2, SetProductMarketTA1, SetProductMarketTA2, + BulkPurchase, + SellShares, + BuyBackShares, SetSmartSupplyUseLeftovers, } from "../Corporation/Actions"; import { CorporationUnlockUpgrades } from "../Corporation/data/CorporationUnlockUpgrades"; @@ -178,6 +181,7 @@ export function NetscriptCorporation( return true; } + function getResearchCost(division: IIndustry, researchName: string): number { const researchTree = IndustryResearchTrees[division.type]; if (researchTree === undefined) throw new Error(`No research tree for industry '${division.type}'`); @@ -436,6 +440,18 @@ export function NetscriptCorporation( const material = getMaterial(divisionName, cityName, materialName); BuyMaterial(material, amt); }, + bulkPurchase: function (adivisionName: any, acityName: any, amaterialName: any, aamt: any): void { + checkAccess("bulkPurchase", 7); + const divisionName = helper.string("bulkPurchase", "divisionName", adivisionName); + if (!hasResearched(getDivision(adivisionName), "Bulk Purchasing")) throw new Error(`You have not researched Bulk Purchasing in ${divisionName}`) + const corporation = getCorporation(); + const cityName = helper.string("bulkPurchase", "cityName", acityName); + const materialName = helper.string("bulkPurchase", "materialName", amaterialName); + const amt = helper.number("bulkPurchase", "amt", aamt); + const warehouse = getWarehouse(divisionName, cityName) + const material = getMaterial(divisionName, cityName, materialName); + BulkPurchase(corporation, warehouse, material, amt); + }, makeProduct: function ( adivisionName: any, acityName: any, @@ -813,6 +829,16 @@ export function NetscriptCorporation( const numShares = helper.number("goPublic", "numShares", anumShares); return goPublic(numShares); }, + sellShares: function (anumShares: any): number { + checkAccess("acceptInvestmentOffer"); + const numShares = helper.number("sellStock", "numShares", anumShares); + return SellShares(getCorporation(), player, numShares); + }, + buyBackShares: function (anumShares: any): boolean { + checkAccess("acceptInvestmentOffer"); + const numShares = helper.number("buyStock", "numShares", anumShares); + return BuyBackShares(getCorporation(), player, numShares); + }, bribe: function (afactionName: string, aamountCash: any, aamountShares: any): boolean { checkAccess("bribe"); const factionName = helper.string("bribe", "factionName", afactionName); diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 661a1f230..25cd9eaa8 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -6382,6 +6382,14 @@ export interface WarehouseAPI { * @param amt - Amount of material to buy */ buyMaterial(divisionName: string, cityName: string, materialName: string, amt: number): void; + /** + * Set material to bulk buy + * @param divisionName - Name of the division + * @param cityName - Name of the city + * @param materialName - Name of the material + * @param amt - Amount of material to buy + */ + bulkPurchase(divisionName: string, cityName: string, materialName: string, amt: number): void; /** * Get warehouse data * @param divisionName - Name of the division @@ -6622,6 +6630,18 @@ export interface Corporation extends WarehouseAPI, OfficeAPI { * @param percent - Percent of profit to issue as dividends. */ issueDividends(percent: number): void; + /** + * Buyback Shares + * @param amount - Amount of shares to buy back. + * + */ + buyBackShares(amount: number): void; + /** + * Sell Shares + * @param amount - Amount of shares to sell. + * + */ + sellShares(amount: number): void; } /**