added buybackShares, sellShares and bulkPurchase

This commit is contained in:
phyzical
2022-01-17 18:03:29 +08:00
parent b742fae1c6
commit d50e199171
6 changed files with 153 additions and 95 deletions

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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 {
<Button onClick={purchaseMaterial}>Confirm</Button>
<Button onClick={clearPurchase}>Clear Purchase</Button>
{division.hasResearch("Bulk Purchasing") && (
<BulkPurchase onClose={props.onClose} mat={props.mat} warehouse={props.warehouse} />
<BulkPurchaseSection onClose={props.onClose} mat={props.mat} warehouse={props.warehouse} />
)}
</>
</Modal>

View File

@@ -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
<Money money={profit} />. The corporation's stock price fell to&nbsp; <Money money={corp.sharePrice} />
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
<Money money={profit} />. The corporation's stock price fell to&nbsp; <Money money={corp.sharePrice} />
as a result of dilution.
</>,
);
props.rerender();
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {

View File

@@ -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);

View File

@@ -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;
}
/**