From 216500ed327abcdaaee52b21f9e73722c90a2175 Mon Sep 17 00:00:00 2001 From: catloversg <152669316+catloversg@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:19:47 +0700 Subject: [PATCH] CORPORATION: Add a new API to sell a division (#1210) Also refactoring around use of "player" variable (whether it is capitalized or not). --- src/Corporation/Actions.ts | 32 +++++++++ src/Corporation/helpers.ts | 7 ++ src/Corporation/ui/Overview.tsx | 10 +-- .../ui/modals/CreateCorporationModal.tsx | 54 +++++++------- .../ui/modals/SellCorporationModal.tsx | 72 ------------------- src/Locations/ui/SpecialLocation.tsx | 2 +- src/Netscript/RamCostGenerator.ts | 1 + src/NetscriptFunctions/Corporation.ts | 39 ++++------ src/NetscriptFunctions/Extra.ts | 2 +- src/NetscriptFunctions/Formulas.ts | 6 +- src/NetscriptFunctions/Hacknet.ts | 28 ++++---- src/NetscriptFunctions/StockMarket.ts | 2 +- src/ScriptEditor/NetscriptDefinitions.d.ts | 8 +++ 13 files changed, 111 insertions(+), 152 deletions(-) delete mode 100644 src/Corporation/ui/modals/SellCorporationModal.tsx diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts index aaaf5973c..6b2f9680a 100644 --- a/src/Corporation/Actions.ts +++ b/src/Corporation/Actions.ts @@ -21,8 +21,40 @@ import { sellSharesFailureReason, buybackSharesFailureReason, issueNewSharesFailureReason, + costOfCreatingCorporation, } from "./helpers"; import { PositiveInteger } from "../types"; +import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; + +export function createCorporation(corporationName: string, selfFund: boolean, restart: boolean): boolean { + if (!Player.canAccessCorporation()) { + return false; + } + if (Player.corporation && !restart) { + return false; + } + if (!corporationName) { + return false; + } + if (Player.bitNodeN !== 3 && !selfFund) { + throw new Error("Cannot use seed funds outside of BitNode 3"); + } + if (currentNodeMults.CorporationSoftcap < 0.15) { + throw new Error(`You cannot create a corporation in BitNode ${Player.bitNodeN}`); + } + + if (selfFund) { + const cost = costOfCreatingCorporation(restart); + if (!Player.canAfford(cost)) { + return false; + } + Player.startCorporation(corporationName, false); + Player.loseMoney(cost, "corporation"); + } else { + Player.startCorporation(corporationName, true); + } + return true; +} export function NewDivision(corporation: Corporation, industry: IndustryType, name: string): void { if (corporation.divisions.size >= corporation.maxDivisions) diff --git a/src/Corporation/helpers.ts b/src/Corporation/helpers.ts index d82ff7a0e..b515cd67f 100644 --- a/src/Corporation/helpers.ts +++ b/src/Corporation/helpers.ts @@ -5,6 +5,13 @@ import { Corporation } from "./Corporation"; import { CorpUpgrade } from "./data/CorporationUpgrades"; import * as corpConstants from "./data/Constants"; +export function costOfCreatingCorporation(restart: boolean): number { + if (restart && !Player.corporation?.seedFunded) { + return 50e9; + } + return 150e9; +} + export function calculateUpgradeCost(corporation: Corporation, upgrade: CorpUpgrade, amount: PositiveInteger): number { const priceMult = upgrade.priceMult; const level = corporation.upgrades[upgrade.name].level; diff --git a/src/Corporation/ui/Overview.tsx b/src/Corporation/ui/Overview.tsx index 97bce0bcb..3f7d41c51 100644 --- a/src/Corporation/ui/Overview.tsx +++ b/src/Corporation/ui/Overview.tsx @@ -28,11 +28,11 @@ import Box from "@mui/material/Box"; import Paper from "@mui/material/Paper"; import Grid from "@mui/material/Grid"; import { MultiplierButtons } from "./MultiplierButtons"; -import { SellCorporationModal } from "./modals/SellCorporationModal"; import { SellDivisionModal } from "./modals/SellDivisionModal"; import { getRecordKeys } from "../../Types/Record"; import { PositiveInteger } from "../../types"; import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip"; +import { CreateCorporationModal } from "./modals/CreateCorporationModal"; interface IProps { rerender: () => void; @@ -319,16 +319,12 @@ function SellDivisionButton(): React.ReactElement { function RestartButton(): React.ReactElement { const [open, setOpen] = useState(false); - function restart(): void { - setOpen(true); - } - return ( <> - + setOpen(true)}> Sell CEO position - setOpen(false)} /> + setOpen(false)} restart={true} /> ); } diff --git a/src/Corporation/ui/modals/CreateCorporationModal.tsx b/src/Corporation/ui/modals/CreateCorporationModal.tsx index 023f2e0e6..d2aa9500a 100644 --- a/src/Corporation/ui/modals/CreateCorporationModal.tsx +++ b/src/Corporation/ui/modals/CreateCorporationModal.tsx @@ -9,18 +9,21 @@ import { Player } from "@player"; import Typography from "@mui/material/Typography"; import { ButtonWithTooltip } from "../../../ui/Components/ButtonWithTooltip"; import TextField from "@mui/material/TextField"; +import { createCorporation } from "../../Actions"; +import { costOfCreatingCorporation } from "../../helpers"; interface IProps { open: boolean; onClose: () => void; + restart: boolean; } export function CreateCorporationModal(props: IProps): React.ReactElement { - const canSelfFund = Player.canAfford(150e9); + const cost = costOfCreatingCorporation(props.restart); + const canSelfFund = Player.canAfford(cost); const [name, setName] = useState(""); - if (!Player.canAccessCorporation() || Player.corporation) { - props.onClose(); + if (!Player.canAccessCorporation() || (Player.corporation && !props.restart)) { return <>; } @@ -30,24 +33,10 @@ export function CreateCorporationModal(props: IProps): React.ReactElement { setName(event.target.value); } - function selfFund(): void { - if (!canSelfFund) return; - if (name == "") return; - - Player.startCorporation(name, false); - Player.loseMoney(150e9, "corporation"); - - props.onClose(); - Router.toPage(Page.Corporation); - } - - function seed(): void { - if (name == "") { + function createCorporationWithUI(corporationName: string, selfFund: boolean): void { + if (!createCorporation(corporationName, selfFund, props.restart)) { return; } - - Player.startCorporation(name, true); - props.onClose(); Router.toPage(Page.Corporation); } @@ -55,30 +44,39 @@ export function CreateCorporationModal(props: IProps): React.ReactElement { return ( - Would you like to start a corporation? This will require for - registration and initial funding.{" "} - {Player.bitNodeN === 3 && ( + {!props.restart ? ( <> - This can either be self-funded, or you can obtain the seed money from the government - in exchange for {formatShares(500e6)} shares (a 33.3% stake in the company). + Would you like to start a corporation? This will require for + registration and initial funding.{" "} + {Player.bitNodeN === 3 && ( + <> + This can either be self-funded, or you can obtain the seed money from the + government in exchange for {formatShares(500e6)} shares (a 33.3% stake in the company). + + )} + + ) : ( + <> + Would you like to sell your position as CEO and start a new corporation? Everything from your current + corporation will be gone and you start fresh. )}

- If you would like to start one, please enter a name for your corporation below: + If you would like to start {props.restart ? "a new" : ""} one, please enter a name for your corporation below:

{Player.bitNodeN === 3 && ( - + createCorporationWithUI(name, false)} disabledTooltip={disabledTextForNoName}> Use seed money )} createCorporationWithUI(name, true)} disabledTooltip={disabledTextForNoName || (canSelfFund ? "" : "Insufficient player funds")} > - Self-Fund () + Self-Fund ()
); diff --git a/src/Corporation/ui/modals/SellCorporationModal.tsx b/src/Corporation/ui/modals/SellCorporationModal.tsx deleted file mode 100644 index d6d701164..000000000 --- a/src/Corporation/ui/modals/SellCorporationModal.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React, { useState } from "react"; - -import { Money } from "../../../ui/React/Money"; -import { Modal } from "../../../ui/React/Modal"; -import { Router } from "../../../ui/GameRoot"; -import { Page } from "../../../ui/Router"; -import { Player } from "@player"; -import Typography from "@mui/material/Typography"; -import Button from "@mui/material/Button"; -import TextField from "@mui/material/TextField"; - -interface IProps { - open: boolean; - onClose: () => void; -} - -export function SellCorporationModal(props: IProps): React.ReactElement { - let cost = 150e9; - if (!Player.corporation?.seedFunded) { - cost /= 3; - } - const canSelfFund = Player.canAfford(cost); - - const [name, setName] = useState(""); - function onChange(event: React.ChangeEvent): void { - setName(event.target.value); - } - - function selfFund(): void { - if (!canSelfFund) return; - if (name == "") return; - - Player.startCorporation(name, false); - Player.loseMoney(cost, "corporation"); - - props.onClose(); - Router.toPage(Page.Corporation); - } - - function seed(): void { - if (name == "") { - return; - } - - Player.startCorporation(name, true); - - props.onClose(); - Router.toPage(Page.Corporation); - } - - return ( - - - Would you like to sell your position as CEO and start a new corporation? Everything from your current - corporation will be gone and you start fresh. -
-
- If you would like to start new one, please enter a name for your corporation below: -
-
- - {Player.bitNodeN === 3 && ( - - )} - -
- ); -} diff --git a/src/Locations/ui/SpecialLocation.tsx b/src/Locations/ui/SpecialLocation.tsx index 2018caeb3..6f776eafb 100644 --- a/src/Locations/ui/SpecialLocation.tsx +++ b/src/Locations/ui/SpecialLocation.tsx @@ -144,7 +144,7 @@ export function SpecialLocation(props: SpecialLocationProps): React.ReactElement - setOpen(false)} /> + setOpen(false)} restart={false} /> ); } diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index e8e3a8a45..f8e9228c9 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -449,6 +449,7 @@ const corporation = { hasResearched: RamCostConstants.CorporationInfo, setAutoJobAssignment: RamCostConstants.CorporationAction, getOfficeSizeUpgradeCost: RamCostConstants.CorporationInfo, + sellDivision: RamCostConstants.CorporationAction, } as const; /** RamCosts guaranteed to match ns structure 1:1 (aside from args and enums). diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts index d2b86facb..ed29006ab 100644 --- a/src/NetscriptFunctions/Corporation.ts +++ b/src/NetscriptFunctions/Corporation.ts @@ -1,4 +1,4 @@ -import { Player, Player as player } from "../Player"; +import { Player } from "@player"; import { OfficeSpace } from "../Corporation/OfficeSpace"; import { Product } from "../Corporation/Product"; @@ -50,6 +50,8 @@ import { LimitMaterialProduction, LimitProductProduction, UpgradeWarehouseCost, + createCorporation, + removeDivision, } from "../Corporation/Actions"; import { CorpUnlocks } from "../Corporation/data/CorporationUnlocks"; import { CorpUpgrades } from "../Corporation/data/CorporationUpgrades"; @@ -58,7 +60,6 @@ import { IndustriesData, IndustryResearchTrees } from "../Corporation/data/Indus import * as corpConstants from "../Corporation/data/Constants"; import { ResearchMap } from "../Corporation/ResearchMap"; import { Factions } from "../Faction/Factions"; -import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; import { InternalAPI, NetscriptContext, setRemovedFunctions } from "../Netscript/APIWrapper"; import { helpers } from "../Netscript/NetscriptHelpers"; import { getEnumHelper } from "../utils/EnumHelper"; @@ -68,24 +69,6 @@ import { PositiveInteger } from "../types"; import { getRecordKeys } from "../Types/Record"; export function NetscriptCorporation(): InternalAPI { - function createCorporation(corporationName: string, selfFund = true): boolean { - if (!player.canAccessCorporation() || player.corporation) return false; - if (!corporationName) return false; - if (player.bitNodeN !== 3 && !selfFund) throw new Error("cannot use seed funds outside of BitNode 3"); - if (currentNodeMults.CorporationSoftcap < 0.15) - throw new Error(`You cannot create a corporation in Bitnode ${player.bitNodeN}`); - - if (selfFund) { - if (!player.canAfford(150e9)) return false; - - player.startCorporation(corporationName, false); - player.loseMoney(150e9, "corporation"); - } else { - player.startCorporation(corporationName, true); - } - return true; - } - function hasUnlock(unlockName: CorpUnlockName): boolean { const corporation = getCorporation(); return corporation.unlocks.has(unlockName); @@ -128,7 +111,7 @@ export function NetscriptCorporation(): InternalAPI { const faction = Factions[factionName]; const info = faction.getInfo(); if (!info.offersWork()) return false; - if (player.hasGangWith(factionName)) return false; + if (Player.hasGangWith(factionName)) return false; const repGain = amountCash / corpConstants.bribeAmountPerReputation; faction.playerReputation += repGain; @@ -138,7 +121,7 @@ export function NetscriptCorporation(): InternalAPI { } function getCorporation(): Corporation { - const corporation = player.corporation; + const corporation = Player.corporation; if (corporation === null) throw new Error("cannot be called without a corporation"); return corporation; } @@ -178,9 +161,9 @@ export function NetscriptCorporation(): InternalAPI { } function checkAccess(ctx: NetscriptContext, api?: CorpUnlockName): void { - if (!player.corporation) throw helpers.errorMessage(ctx, "Must own a corporation."); + if (!Player.corporation) throw helpers.errorMessage(ctx, "Must own a corporation."); if (!api) return; - if (!player.corporation.unlocks.has(api)) { + if (!Player.corporation.unlocks.has(api)) { throw helpers.errorMessage(ctx, "You do not have access to this API."); } } @@ -732,7 +715,7 @@ export function NetscriptCorporation(): InternalAPI { (_corporationName, _selfFund = true): boolean => { const corporationName = helpers.string(ctx, "corporationName", _corporationName); const selfFund = !!_selfFund; - return createCorporation(corporationName, selfFund); + return createCorporation(corporationName, selfFund, false); }, hasUnlock: (ctx) => (_unlockName) => { checkAccess(ctx); @@ -803,6 +786,12 @@ export function NetscriptCorporation(): InternalAPI { CorporationPromise.promise = new Promise((res) => (CorporationPromise.resolve = res)); return CorporationPromise.promise; }, + sellDivision: (ctx) => (_divisionName) => { + checkAccess(ctx); + const corporation = getCorporation(); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + removeDivision(corporation, divisionName); + }, }; // Removed functions diff --git a/src/NetscriptFunctions/Extra.ts b/src/NetscriptFunctions/Extra.ts index dc7d4ded1..ef0deb7da 100644 --- a/src/NetscriptFunctions/Extra.ts +++ b/src/NetscriptFunctions/Extra.ts @@ -1,4 +1,4 @@ -import { Player } from "../Player"; +import { Player } from "@player"; import { Exploit } from "../Exploits/Exploit"; import * as bcrypt from "bcryptjs"; import { Apr1Events as devMenu } from "../ui/Apr1"; diff --git a/src/NetscriptFunctions/Formulas.ts b/src/NetscriptFunctions/Formulas.ts index efdcb294a..59965d70b 100644 --- a/src/NetscriptFunctions/Formulas.ts +++ b/src/NetscriptFunctions/Formulas.ts @@ -1,4 +1,4 @@ -import { Player as player } from "../Player"; +import { Player } from "@player"; import { calculateServerGrowth, calculateGrowMoney } from "../Server/formulas/grow"; import { numCycleForGrowthCorrected } from "../Server/ServerHelpers"; import { @@ -62,7 +62,7 @@ import { findCrime } from "../Crime/CrimeHelpers"; export function NetscriptFormulas(): InternalAPI { const checkFormulasAccess = function (ctx: NetscriptContext): void { - if (!player.hasProgram(CompletedProgramName.formulas)) { + if (!Player.hasProgram(CompletedProgramName.formulas)) { throw helpers.errorMessage(ctx, `Requires Formulas.exe to run.`); } }; @@ -325,7 +325,7 @@ export function NetscriptFormulas(): InternalAPI { const upgName = helpers.string(ctx, "upgName", _upgName); const level = helpers.number(ctx, "level", _level); checkFormulasAccess(ctx); - const upg = player.hashManager.getUpgrade(upgName); + const upg = Player.hashManager.getUpgrade(upgName); if (!upg) { throw helpers.errorMessage(ctx, `Invalid Hash Upgrade: ${upgName}`); } diff --git a/src/NetscriptFunctions/Hacknet.ts b/src/NetscriptFunctions/Hacknet.ts index cbe6a433a..01df2e755 100644 --- a/src/NetscriptFunctions/Hacknet.ts +++ b/src/NetscriptFunctions/Hacknet.ts @@ -1,4 +1,4 @@ -import { Player as player } from "../Player"; +import { Player } from "@player"; import { HacknetServerConstants } from "../Hacknet/data/Constants"; import { getCostOfNextHacknetNode, @@ -25,12 +25,12 @@ import { helpers } from "../Netscript/NetscriptHelpers"; export function NetscriptHacknet(): InternalAPI { // Utility function to get Hacknet Node object const getHacknetNode = function (ctx: NetscriptContext, i: number): HacknetNode | HacknetServer { - if (i < 0 || i >= player.hacknetNodes.length) { + if (i < 0 || i >= Player.hacknetNodes.length) { throw helpers.errorMessage(ctx, "Index specified for Hacknet Node is out-of-bounds: " + i); } if (hasHacknetServers()) { - const hi = player.hacknetNodes[i]; + const hi = Player.hacknetNodes[i]; if (typeof hi !== "string") throw new Error("hacknet node was not a string"); const hserver = GetServer(hi); if (!(hserver instanceof HacknetServer)) throw new Error("hacknet server was not actually hacknet server"); @@ -43,7 +43,7 @@ export function NetscriptHacknet(): InternalAPI { return hserver; } else { - const node = player.hacknetNodes[i]; + const node = Player.hacknetNodes[i]; if (!(node instanceof HacknetNode)) throw new Error("hacknet node was not node."); return node; } @@ -51,7 +51,7 @@ export function NetscriptHacknet(): InternalAPI { return { numNodes: () => () => { - return player.hacknetNodes.length; + return Player.hacknetNodes.length; }, maxNumNodes: () => () => { if (hasHacknetServers()) { @@ -140,7 +140,7 @@ export function NetscriptHacknet(): InternalAPI { const i = helpers.number(ctx, "i", _i); const n = helpers.number(ctx, "n", _n); const node = getHacknetNode(ctx, i); - return node.calculateLevelUpgradeCost(n, player.mults.hacknet_node_level_cost); + return node.calculateLevelUpgradeCost(n, Player.mults.hacknet_node_level_cost); }, getRamUpgradeCost: (ctx) => @@ -148,7 +148,7 @@ export function NetscriptHacknet(): InternalAPI { const i = helpers.number(ctx, "i", _i); const n = helpers.number(ctx, "n", _n); const node = getHacknetNode(ctx, i); - return node.calculateRamUpgradeCost(n, player.mults.hacknet_node_ram_cost); + return node.calculateRamUpgradeCost(n, Player.mults.hacknet_node_ram_cost); }, getCoreUpgradeCost: (ctx) => @@ -156,7 +156,7 @@ export function NetscriptHacknet(): InternalAPI { const i = helpers.number(ctx, "i", _i); const n = helpers.number(ctx, "n", _n); const node = getHacknetNode(ctx, i); - return node.calculateCoreUpgradeCost(n, player.mults.hacknet_node_core_cost); + return node.calculateCoreUpgradeCost(n, Player.mults.hacknet_node_core_cost); }, getCacheUpgradeCost: (ctx) => @@ -177,13 +177,13 @@ export function NetscriptHacknet(): InternalAPI { if (!hasHacknetServers()) { return 0; } - return player.hashManager.hashes; + return Player.hashManager.hashes; }, hashCapacity: () => () => { if (!hasHacknetServers()) { return 0; } - return player.hashManager.capacity; + return Player.hashManager.capacity; }, hashCost: (ctx) => @@ -194,7 +194,7 @@ export function NetscriptHacknet(): InternalAPI { return Infinity; } - return player.hashManager.getUpgradeCost(upgName, count); + return Player.hashManager.getUpgradeCost(upgName, count); }, spendHashes: (ctx) => @@ -219,7 +219,7 @@ export function NetscriptHacknet(): InternalAPI { }, getHashUpgradeLevel: (ctx) => (_upgName) => { const upgName = helpers.string(ctx, "upgName", _upgName); - const level = player.hashManager.upgrades[upgName]; + const level = Player.hashManager.upgrades[upgName]; if (level === undefined) { throw helpers.errorMessage(ctx, `Invalid Hash Upgrade: ${upgName}`); } @@ -229,13 +229,13 @@ export function NetscriptHacknet(): InternalAPI { if (!hasHacknetServers()) { return 1; } - return player.hashManager.getStudyMult(); + return Player.hashManager.getStudyMult(); }, getTrainingMult: () => () => { if (!hasHacknetServers()) { return 1; } - return player.hashManager.getTrainingMult(); + return Player.hashManager.getTrainingMult(); }, }; } diff --git a/src/NetscriptFunctions/StockMarket.ts b/src/NetscriptFunctions/StockMarket.ts index c4069e880..661a43598 100644 --- a/src/NetscriptFunctions/StockMarket.ts +++ b/src/NetscriptFunctions/StockMarket.ts @@ -1,4 +1,4 @@ -import { Player } from "../Player"; +import { Player } from "@player"; import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling"; import { StockMarket, diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index e00155f11..a224b0f67 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -8181,6 +8181,14 @@ export interface Corporation extends WarehouseAPI, OfficeAPI { * ``` */ nextUpdate(): Promise; + + /** + * Sell a division + * @remarks + * RAM cost: 20 GB + * + * @param divisionName - Name of the division */ + sellDivision(divisionName: string): void; } /** Product rating information