From d50e199171222ad04d4c4231b96eb8e9983472f5 Mon Sep 17 00:00:00 2001 From: phyzical Date: Mon, 17 Jan 2022 18:03:29 +0800 Subject: [PATCH] added buybackShares, sellShares and bulkPurchase --- src/Corporation/Actions.ts | 53 +++++++++++++ src/Corporation/ui/BuybackSharesModal.tsx | 24 +++--- src/Corporation/ui/PurchaseMaterialModal.tsx | 30 ++----- src/Corporation/ui/SellSharesModal.tsx | 43 ++++------ src/NetscriptFunctions/Corporation.ts | 82 +++++++++++++------- src/ScriptEditor/NetscriptDefinitions.d.ts | 16 ++++ 6 files changed, 153 insertions(+), 95 deletions(-) diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts index 1da5640f9..7998d9052 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!"); + 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!"); + if (corporation.funds < (numShares * corporation.sharePrice)) throw new Error("You cant afford that many shares!"); + + const buybackPrice = corporation.sharePrice * 1.1; + 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; diff --git a/src/Corporation/ui/BuybackSharesModal.tsx b/src/Corporation/ui/BuybackSharesModal.tsx index d58b01842..93049fc8b 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,22 +38,14 @@ 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) + props.onClose(); + props.rerender(); + } + catch (e) { + dialogBoxCreate(err + ""); } - corp.issuedShares -= shares; - player.loseMoney(shares * buybackPrice, "corporation"); - props.onClose(); - props.rerender(); } function CostIndicator(): React.ReactElement { diff --git a/src/Corporation/ui/PurchaseMaterialModal.tsx b/src/Corporation/ui/PurchaseMaterialModal.tsx index 114e53940..c4c473da1 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,32 +54,16 @@ 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; - } + try { + BulkPurchase(corp, props.warehouse, props.mat, buyAmt); props.onClose(); + } catch (err) { + dialogBoxCreate(err + ""); } } @@ -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 05845801c..a58b1228e 100644 --- a/src/NetscriptFunctions/Corporation.ts +++ b/src/NetscriptFunctions/Corporation.ts @@ -49,6 +49,9 @@ import { SetMaterialMarketTA2, SetProductMarketTA1, SetProductMarketTA2, + BulkPurchase, + SellShares, + BuyBackShares, } from "../Corporation/Actions"; import { CorporationUnlockUpgrades } from "../Corporation/data/CorporationUnlockUpgrades"; import { CorporationUpgrades } from "../Corporation/data/CorporationUpgrades"; @@ -60,6 +63,7 @@ import { CorporationConstants } from "../Corporation/data/Constants"; import { IndustryUpgrades } from "../Corporation/IndustryUpgrades"; import { ResearchMap } from "../Corporation/ResearchMap"; import { Factions } from "../Faction/Factions"; +import { sellStock } from '../StockMarket/BuyingAndSelling'; export function NetscriptCorporation( player: IPlayer, @@ -131,11 +135,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]; @@ -145,7 +149,7 @@ export function NetscriptCorporation( return { funds: funding, shares: investShares, - round: corporation.fundingRound + 1 // Make more readable + round: corporation.fundingRound + 1 // Make more readable }; } @@ -177,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}'`); @@ -192,7 +197,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; @@ -270,25 +275,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 = { @@ -420,6 +425,17 @@ 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 corporation = getCorporation(); + const divisionName = helper.string("bulkPurchase", "divisionName", adivisionName); + 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, @@ -770,24 +786,34 @@ 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 { + 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); const amountCash = helper.number("bribe", "amountCash", aamountCash); diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index d99fa5558..6a57f4ff5 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -6239,6 +6239,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 @@ -6479,6 +6487,14 @@ export interface Corporation extends WarehouseAPI, OfficeAPI { * @param percent - Percent of profit to issue as dividends. */ issueDividends(percent: number): void; + /** + * Buyback Shares + */ + buyBackShares(amt: number): void; + /** + * Sell Shares + */ + sellShares(amt: number): void; } /**