From b8faa9dc0b87a0864fa46dba81243ad47a26d1ca Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Thu, 23 Sep 2021 18:47:43 -0400 Subject: [PATCH] convert player to ts --- src/Augmentation/AugmentationHelpers.d.ts | 1 + src/Bladeburner/ui/BladeburnerRoot.tsx | 14 +- src/Corporation/ui/CorporationRoot.tsx | 19 +- src/Crime/Crime.ts | 5 +- src/DarkWeb/DarkWeb.tsx | 3 +- src/DevMenu/ui/Bladeburner.tsx | 33 +- src/Exploits/Exploit.ts | 4 +- src/Fconf/Fconf.d.ts | 2 - src/Fconf/Fconf.js | 268 -------- src/Fconf/FconfSettings.ts | 10 - src/Gang/ui/GangRoot.tsx | 14 +- src/Hacknet/HacknetHelpers.tsx | 34 +- src/NetscriptFunctions.js | 2 +- src/PersonObjects/IPlayer.ts | 82 ++- src/PersonObjects/Player/PlayerObject.js | 224 ------- src/PersonObjects/Player/PlayerObject.ts | 591 ++++++++++++++++++ .../Player/PlayerObjectGangMethods.ts | 22 +- ...ods.jsx => PlayerObjectGeneralMethods.tsx} | 480 +++++++------- .../Player/PlayerObjectServerMethods.ts | 12 +- src/Player.d.ts | 3 - src/{Player.js => Player.ts} | 6 +- src/Programs/data/ProgramsMetadata.ts | 5 +- src/Script/RunningScript.ts | 1 - src/ScriptEditor/ui/Root.tsx | 12 +- src/Sidebar/ui/SidebarRoot.tsx | 1 - src/Terminal.d.ts | 1 - src/{Terminal.jsx => Terminal.ts} | 0 src/Terminal/Terminal.ts | 61 +- src/Terminal/commands/nano.ts | 7 +- src/Terminal/ui/TerminalRoot.tsx | 5 +- src/ui/GameRoot.tsx | 6 +- src/ui/MainMenu/Headers.ts | 177 ------ src/ui/MainMenu/Links.ts | 98 --- src/ui/React/GameOptionsRoot.tsx | 4 +- 34 files changed, 1052 insertions(+), 1155 deletions(-) delete mode 100644 src/Fconf/Fconf.d.ts delete mode 100644 src/Fconf/Fconf.js delete mode 100644 src/Fconf/FconfSettings.ts delete mode 100644 src/PersonObjects/Player/PlayerObject.js create mode 100644 src/PersonObjects/Player/PlayerObject.ts rename src/PersonObjects/Player/{PlayerObjectGeneralMethods.jsx => PlayerObjectGeneralMethods.tsx} (84%) delete mode 100644 src/Player.d.ts rename src/{Player.js => Player.ts} (85%) delete mode 100644 src/Terminal.d.ts rename src/{Terminal.jsx => Terminal.ts} (100%) delete mode 100644 src/ui/MainMenu/Headers.ts delete mode 100644 src/ui/MainMenu/Links.ts diff --git a/src/Augmentation/AugmentationHelpers.d.ts b/src/Augmentation/AugmentationHelpers.d.ts index c20c69364..80a1be9cc 100644 --- a/src/Augmentation/AugmentationHelpers.d.ts +++ b/src/Augmentation/AugmentationHelpers.d.ts @@ -1,2 +1,3 @@ export declare function isRepeatableAug(aug: Augmentation): boolean; export declare function installAugmentations(): void; +export declare function applyAugmentation(aug: Augmentation, reapply?: boolean): void; diff --git a/src/Bladeburner/ui/BladeburnerRoot.tsx b/src/Bladeburner/ui/BladeburnerRoot.tsx index 35af06174..e0c62747c 100644 --- a/src/Bladeburner/ui/BladeburnerRoot.tsx +++ b/src/Bladeburner/ui/BladeburnerRoot.tsx @@ -6,13 +6,11 @@ import { AllPages } from "./AllPages"; import { use } from "../../ui/Context"; import { IBladeburner } from "../IBladeburner"; -interface IProps { - bladeburner: IBladeburner; -} - -export function BladeburnerRoot(props: IProps): React.ReactElement { +export function BladeburnerRoot(): React.ReactElement { const player = use.Player(); const router = use.Router(); + const bladeburner = player.bladeburner; + if (bladeburner === null) return <>; return (
@@ -24,9 +22,9 @@ export function BladeburnerRoot(props: IProps): React.ReactElement { border: "1px solid white", }} > - +
- +
- +
); diff --git a/src/Corporation/ui/CorporationRoot.tsx b/src/Corporation/ui/CorporationRoot.tsx index 8784a35a7..0ad51fd23 100644 --- a/src/Corporation/ui/CorporationRoot.tsx +++ b/src/Corporation/ui/CorporationRoot.tsx @@ -10,6 +10,7 @@ import { ICorporation } from "../ICorporation"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { MainPanel } from "./MainPanel"; import { Industries } from "../IndustryData"; +import { use } from "../../ui/Context"; interface IExpandButtonProps { corp: ICorporation; @@ -38,12 +39,10 @@ function ExpandButton(props: IExpandButtonProps): React.ReactElement { return ; } -interface IProps { - corp: ICorporation; - player: IPlayer; -} - -export function CorporationRoot(props: IProps): React.ReactElement { +export function CorporationRoot(): React.ReactElement { + const player = use.Player(); + const corporation = player.corporation; + if (corporation === null) return <>; const setRerender = useState(false)[1]; function rerender(): void { setRerender((old) => !old); @@ -62,9 +61,9 @@ export function CorporationRoot(props: IProps): React.ReactElement { current={divisionName === "Overview"} key={"overview"} onClick={() => setDivisionName("Overview")} - text={props.corp.name} + text={corporation.name} /> - {props.corp.divisions.map((division: IIndustry) => ( + {corporation.divisions.map((division: IIndustry) => ( ))} - + - + ); } diff --git a/src/Crime/Crime.ts b/src/Crime/Crime.ts index 798b8e262..75db4bb42 100644 --- a/src/Crime/Crime.ts +++ b/src/Crime/Crime.ts @@ -2,6 +2,7 @@ import { CONSTANTS } from "../Constants"; import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayerOrSleeve } from "../PersonObjects/IPlayerOrSleeve"; import { IRouter } from "../ui/Router"; +import { WorkerScript } from "../Netscript/WorkerScript"; export interface IConstructorParams { hacking_success_weight?: number; @@ -86,7 +87,7 @@ export class Crime { this.kills = params.kills ? params.kills : 0; } - commit(router: IRouter, p: IPlayer, div = 1, singParams: any = null): number { + commit(router: IRouter, p: IPlayer, div = 1, workerScript: WorkerScript | null = null): number { if (div <= 0) { div = 1; } @@ -101,7 +102,7 @@ export class Crime { this.charisma_exp / div, this.money / div, this.time, - singParams, + workerScript, ); return this.time; diff --git a/src/DarkWeb/DarkWeb.tsx b/src/DarkWeb/DarkWeb.tsx index 5473bf036..2e339946b 100644 --- a/src/DarkWeb/DarkWeb.tsx +++ b/src/DarkWeb/DarkWeb.tsx @@ -14,7 +14,8 @@ export function checkIfConnectedToDarkweb(): void { if (!isValidIPAddress(darkwebIp)) { return; } - if (darkwebIp == Player.getCurrentServer().ip) { + const server = Player.getCurrentServer(); + if (server !== null && darkwebIp == server.ip) { Terminal.print( "You are now connected to the dark web. From the dark web you can purchase illegal items. " + "Use the 'buy -l' command to display a list of all the items you can buy. Use 'buy [item-name] " + diff --git a/src/DevMenu/ui/Bladeburner.tsx b/src/DevMenu/ui/Bladeburner.tsx index 0a435dfa5..81082a8b9 100644 --- a/src/DevMenu/ui/Bladeburner.tsx +++ b/src/DevMenu/ui/Bladeburner.tsx @@ -15,43 +15,42 @@ interface IProps { } export function Bladeburner(props: IProps): React.ReactElement { + const bladeburner = props.player.bladeburner; + if (bladeburner === null) return <>; function modifyBladeburnerRank(modify: number): (x: number) => void { return function (rank: number): void { - if (props.player.bladeburner) { - props.player.bladeburner.changeRank(props.player, rank * modify); - } + if (!bladeburner) return; + bladeburner.changeRank(props.player, rank * modify); }; } function resetBladeburnerRank(): void { - props.player.bladeburner.rank = 0; - props.player.bladeburner.maxRank = 0; + if (!bladeburner) return; + bladeburner.rank = 0; + bladeburner.maxRank = 0; } function addTonsBladeburnerRank(): void { - if (props.player.bladeburner) { - props.player.bladeburner.changeRank(props.player, bigNumber); - } + if (!bladeburner) return; + + bladeburner.changeRank(props.player, bigNumber); } function modifyBladeburnerCycles(modify: number): (x: number) => void { return function (cycles: number): void { - if (props.player.bladeburner) { - props.player.bladeburner.storedCycles += cycles * modify; - } + if (!bladeburner) return; + bladeburner.storedCycles += cycles * modify; }; } function resetBladeburnerCycles(): void { - if (props.player.bladeburner) { - props.player.bladeburner.storedCycles = 0; - } + if (!bladeburner) return; + bladeburner.storedCycles = 0; } function addTonsBladeburnerCycles(): void { - if (props.player.bladeburner) { - props.player.bladeburner.storedCycles += bigNumber; - } + if (!bladeburner) return; + bladeburner.storedCycles += bigNumber; } return ( diff --git a/src/Exploits/Exploit.ts b/src/Exploits/Exploit.ts index 77da933c7..1d5f23482 100644 --- a/src/Exploits/Exploit.ts +++ b/src/Exploits/Exploit.ts @@ -35,6 +35,6 @@ export function ExploitName(exploit: string): string { return names[exploit]; } -export function sanitizeExploits(exploits: string[]): string[] { - return exploits.filter((e: string) => Object.keys(Exploit).includes(e)); +export function sanitizeExploits(exploits: Exploit[]): Exploit[] { + return exploits.filter((e: Exploit) => Object.keys(Exploit).includes(e)); } diff --git a/src/Fconf/Fconf.d.ts b/src/Fconf/Fconf.d.ts deleted file mode 100644 index fcacfe3f8..000000000 --- a/src/Fconf/Fconf.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare function parseFconfSettings(config: string): void; -export declare function createFconf(): string; diff --git a/src/Fconf/Fconf.js b/src/Fconf/Fconf.js deleted file mode 100644 index a31645858..000000000 --- a/src/Fconf/Fconf.js +++ /dev/null @@ -1,268 +0,0 @@ -import { FconfSettings } from "./FconfSettings"; - -import { parse, Node } from "acorn"; -import { dialogBoxCreate } from "../../utils/DialogBox"; - -var FconfComments = { - ENABLE_BASH_HOTKEYS: - "Improved Bash emulation mode. Setting this to 1 enables several\n" + - "new Terminal shortcuts and features that more closely resemble\n" + - "a real Bash-style shell. Note that when this mode is enabled,\n" + - "the default browser shortcuts are overriden by the new Bash\n" + - "shortcuts.\n\n" + - "To see a full list of the Terminal shortcuts that this enables, see:\n" + - "http://bitburner.readthedocs.io/en/latest/shortcuts.html", - ENABLE_TIMESTAMPS: - "Terminal commands and log entries will be timestamped. The timestamp\n" + "will have the format: M/D h:m", - MAIN_MENU_STYLE: - "Customize the main navigation menu on the left-hand side. Current options:\n\n" + "default, classic, compact", - THEME_BACKGROUND_COLOR: - "Sets the background color for not only the Terminal, but also for\n" + - "most of the game's UI.\n\n" + - "The color must be specified as a pound sign (#) followed by a \n" + - "3-digit or 6-digit hex color code (e.g. #123456). Default color: #000000", - THEME_FONT_COLOR: - "Sets the font color for not only the Terminal, but also for\n" + - "most of the game's UI.\n\n" + - "The color must be specified as a pound sign (#) followed by a \n" + - "3-digit or 6-digit hex color code (e.g. #123456). Default color: #66ff33", - THEME_HIGHLIGHT_COLOR: - "Sets the highlight color for not only the Terminal, but also for \n" + - "most of the game's UI.\n\n" + - "The color must be specified as a pound sign (#) followed by a \n" + - "3-digit or 6-digit hex color code (e.g. #123456). Default color: #ffffff", - THEME_PROMPT_COLOR: - "Sets the prompt color in the Terminal\n\n" + - "The color must be specified as a pound sign (#) followed by a \n" + - "3-digit or 6-digit hex color code (e.g. #123456). Default color: #f92672", - WRAP_INPUT: - "Wrap Terminal Input. If this is enabled, then when a Terminal command is\n" + - "too long and overflows, then it will wrap to the next line instead of\n" + - "side-scrolling\n\n" + - "Note that after you enable/disable this, you'll have to run a command\n" + - "before its effect takes place.", -}; - -const MainMenuStyleOptions = ["default", "classic", "compact"]; - -//Parse Fconf settings from the config text -//Throws an exception if parsing fails -function parseFconfSettings(config) { - var ast = parse(config, { sourceType: "module" }); - var queue = []; - queue.push(ast); - while (queue.length != 0) { - var exp = queue.shift(); - switch (exp.type) { - case "BlockStatement": - case "Program": - for (var i = 0; i < exp.body.length; ++i) { - if (exp.body[i] instanceof Node) { - queue.push(exp.body[i]); - } - } - break; - case "AssignmentExpression": - var setting, value; - if (exp.left != null && exp.left.name != null) { - setting = exp.left.name; - } else { - break; - } - if (exp.right != null && exp.right.raw != null) { - value = exp.right.raw; - } else { - break; - } - parseFconfSetting(setting, value); - break; - default: - break; - } - - for (var prop in exp) { - if (exp.hasOwnProperty(prop)) { - if (exp[prop] instanceof Node) { - queue.push(exp[prop]); - } - } - } - } - - setTheme(); - setMainMenuStyle(); -} - -function parseFconfSetting(setting, value) { - setting = String(setting); - value = String(value); - if (setting == null || value == null || FconfSettings[setting] == null) { - console.warn(`Invalid .fconf setting: ${setting}`); - return; - } - - function sanitizeString(value) { - value = value.toLowerCase(); - if (value.startsWith('"')) { - value = value.slice(1); - } - if (value.endsWith('"')) { - value = value.slice(0, -1); - } - return value; - } - - switch (setting) { - case "ENABLE_BASH_HOTKEYS": - case "ENABLE_TIMESTAMPS": - case "WRAP_INPUT": - // Need to convert entered value to boolean/strings accordingly - var value = value.toLowerCase(); - if (value === "1" || value === "true" || value === "y") { - value = true; - } else { - value = false; - } - FconfSettings[setting] = value; - break; - case "MAIN_MENU_STYLE": - var value = sanitizeString(value); - if (MainMenuStyleOptions.includes(value)) { - FconfSettings[setting] = value; - } else { - dialogBoxCreate(`Invalid option specified for ${setting}. Options: ${MainMenuStyleOptions.toString()}`); - } - break; - case "THEME_BACKGROUND_COLOR": - case "THEME_FONT_COLOR": - case "THEME_HIGHLIGHT_COLOR": - case "THEME_PROMPT_COLOR": - var value = sanitizeString(value); - if (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(value)) { - FconfSettings[setting] = value; - } else { - dialogBoxCreate(`Invalid color specified for ${setting}. Must be a hex color code preceded by a pound (#)`); - } - break; - default: - break; - } -} - -//Create the .fconf file text from the settings -function createFconf() { - var res = ""; - for (var setting in FconfSettings) { - if (FconfSettings.hasOwnProperty(setting)) { - //Setting comments (description) - var comment = FconfComments[setting]; - if (comment == null) { - continue; - } - var comment = comment.split("\n"); - for (var i = 0; i < comment.length; ++i) { - res += "//" + comment[i] + "\n"; - } - - var value = 0; - if (FconfSettings[setting] === true) { - value = "1"; - } else if (FconfSettings[setting] === false) { - value = "0"; - } else { - value = '"' + String(FconfSettings[setting]) + '"'; - } - res += `${setting} = ${value}\n\n`; - } - } - return res; -} - -function loadFconf(saveString) { - let tempFconfSettings = JSON.parse(saveString); - for (var setting in tempFconfSettings) { - if (tempFconfSettings.hasOwnProperty(setting)) { - FconfSettings[setting] = tempFconfSettings[setting]; - } - } - - // Initialize themes/styles after loading - setTheme(); - setMainMenuStyle(); -} - -function setTheme() { - if ( - FconfSettings.THEME_HIGHLIGHT_COLOR == null || - FconfSettings.THEME_FONT_COLOR == null || - FconfSettings.THEME_BACKGROUND_COLOR == null || - FconfSettings.THEME_PROMPT_COLOR == null - ) { - console.error("Cannot find Theme Settings"); - return; - } - if ( - /^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(FconfSettings.THEME_HIGHLIGHT_COLOR) && - /^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(FconfSettings.THEME_FONT_COLOR) && - /^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(FconfSettings.THEME_BACKGROUND_COLOR) && - /^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(FconfSettings.THEME_PROMPT_COLOR) - ) { - // document.body.style.setProperty("--my-highlight-color", FconfSettings.THEME_HIGHLIGHT_COLOR); - // document.body.style.setProperty("--my-font-color", FconfSettings.THEME_FONT_COLOR); - // document.body.style.setProperty("--my-background-color", FconfSettings.THEME_BACKGROUND_COLOR); - // document.body.style.setProperty("--my-prompt-color", FconfSettings.THEME_PROMPT_COLOR); - } -} - -function setMainMenuStyle() { - const mainMenu = document.getElementById("mainmenu"); - const hackingMenuHdr = document.getElementById("hacking-menu-header"); - const characterMenuHdr = document.getElementById("character-menu-header"); - const worldMenuHdr = document.getElementById("world-menu-header"); - const helpMenuHdr = document.getElementById("help-menu-header"); - - function removeAllAccordionHeaderClasses() { - hackingMenuHdr.classList.remove("mainmenu-accordion-header", "mainmenu-accordion-header-classic"); - characterMenuHdr.classList.remove("mainmenu-accordion-header", "mainmenu-accordion-header-classic"); - worldMenuHdr.classList.remove("mainmenu-accordion-header", "mainmenu-accordion-header-classic"); - helpMenuHdr.classList.remove("mainmenu-accordion-header", "mainmenu-accordion-header-classic"); - } - - function addClassToAllAccordionHeaders(clsName) { - hackingMenuHdr.classList.add(clsName); - characterMenuHdr.classList.add(clsName); - worldMenuHdr.classList.add(clsName); - helpMenuHdr.classList.add(clsName); - } - - if (FconfSettings["MAIN_MENU_STYLE"] === "default") { - removeAllAccordionHeaderClasses(); - mainMenu.classList.remove("classic"); - mainMenu.classList.remove("compact"); - addClassToAllAccordionHeaders("mainmenu-accordion-header"); - } else if (FconfSettings["MAIN_MENU_STYLE"] === "classic") { - removeAllAccordionHeaderClasses(); - mainMenu.classList.remove("compact"); - mainMenu.classList.add("classic"); - addClassToAllAccordionHeaders("mainmenu-accordion-header-classic"); - } else if (FconfSettings["MAIN_MENU_STYLE"] === "compact") { - removeAllAccordionHeaderClasses(); - mainMenu.classList.remove("classic"); - mainMenu.classList.add("compact"); - addClassToAllAccordionHeaders("mainmenu-accordion-header-compact"); - } else { - return; - } - - // Click each header twice to reset lol - hackingMenuHdr.click(); - hackingMenuHdr.click(); - characterMenuHdr.click(); - characterMenuHdr.click(); - worldMenuHdr.click(); - worldMenuHdr.click(); - helpMenuHdr.click(); - helpMenuHdr.click(); -} - -export { FconfSettings, createFconf, parseFconfSettings, loadFconf }; diff --git a/src/Fconf/FconfSettings.ts b/src/Fconf/FconfSettings.ts deleted file mode 100644 index 12c5e8a0e..000000000 --- a/src/Fconf/FconfSettings.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const FconfSettings = { - ENABLE_BASH_HOTKEYS: false, - ENABLE_TIMESTAMPS: false, - MAIN_MENU_STYLE: "default", - THEME_BACKGROUND_COLOR: "#000000", - THEME_FONT_COLOR: "#66ff33", - THEME_HIGHLIGHT_COLOR: "#ffffff", - THEME_PROMPT_COLOR: "#f92672", - WRAP_INPUT: false, -}; diff --git a/src/Gang/ui/GangRoot.tsx b/src/Gang/ui/GangRoot.tsx index d746d7626..5ee45804f 100644 --- a/src/Gang/ui/GangRoot.tsx +++ b/src/Gang/ui/GangRoot.tsx @@ -8,13 +8,13 @@ import { use } from "../../ui/Context"; import { Factions } from "../../Faction/Factions"; import { Gang } from "../Gang"; -interface IProps { - gang: Gang; -} - -export function GangRoot(props: IProps): React.ReactElement { +export function GangRoot(): React.ReactElement { const player = use.Player(); const router = use.Router(); + const gang = (function () { + if (player.gang === null) throw new Error("Gang should not be null"); + return player.gang; + })(); const [management, setManagement] = useState(true); const setRerender = useState(false)[1]; @@ -24,7 +24,7 @@ export function GangRoot(props: IProps): React.ReactElement { }, []); function back(): void { - router.toFaction(Factions[props.gang.facName]); + router.toFaction(Factions[gang.facName]); } return ( @@ -46,7 +46,7 @@ export function GangRoot(props: IProps): React.ReactElement { > Gang Territory - {management ? : } + {management ? : } ); } diff --git a/src/Hacknet/HacknetHelpers.tsx b/src/Hacknet/HacknetHelpers.tsx index 38411fdba..d5fd1345a 100644 --- a/src/Hacknet/HacknetHelpers.tsx +++ b/src/Hacknet/HacknetHelpers.tsx @@ -479,13 +479,12 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget: break; } case "Sell for Corporation Funds": { - // This will throw if player doesn't have a corporation - try { - player.corporation.funds = player.corporation.funds.plus(upg.value); - } catch (e) { + const corp = player.corporation; + if (corp === null) { player.hashManager.refundUpgrade(upgName); return false; } + corp.funds = corp.funds.plus(upg.value); break; } case "Reduce Minimum Security": { @@ -530,36 +529,35 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget: } case "Exchange for Corporation Research": { // This will throw if player doesn't have a corporation - try { - for (const division of player.corporation.divisions) { - division.sciResearch.qty += upg.value; - } - } catch (e) { + const corp = player.corporation; + if (corp === null) { player.hashManager.refundUpgrade(upgName); return false; } + for (const division of corp.divisions) { + division.sciResearch.qty += upg.value; + } break; } case "Exchange for Bladeburner Rank": { // This will throw if player isnt in Bladeburner - try { - player.bladeburner.changeRank(player, upg.value); - } catch (e) { + const bladeburner = player.bladeburner; + if (bladeburner === null) { player.hashManager.refundUpgrade(upgName); return false; } + bladeburner.changeRank(player, upg.value); break; } case "Exchange for Bladeburner SP": { - // This will throw if player isn't in Bladeburner - try { - // As long as we don't change `Bladeburner.totalSkillPoints`, this - // shouldn't affect anything else - player.bladeburner.skillPoints += upg.value; - } catch (e) { + // This will throw if player isnt in Bladeburner + const bladeburner = player.bladeburner; + if (bladeburner === null) { player.hashManager.refundUpgrade(upgName); return false; } + + bladeburner.skillPoints += upg.value; break; } case "Generate Coding Contract": { diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index 787e52c30..86627e160 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -3808,7 +3808,7 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeErrorMsg("commitCrime", `Invalid crime: '${crimeRoughName}'`); } workerScript.log("commitCrime", `Attempting to commit ${crime.name}...`); - return crime.commit(Router, Player, 1, { workerscript: workerScript }); + return crime.commit(Router, Player, 1, workerScript); }, getCrimeChance: function (crimeRoughName) { updateDynamicRam("getCrimeChance", getRamCost("getCrimeChance")); diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index 993d8a064..393a8995b 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -26,6 +26,8 @@ import { IGang } from "../Gang/IGang"; import { IBladeburner } from "../Bladeburner/IBladeburner"; import { ICodingContractReward } from "../CodingContracts"; import { IRouter } from "../ui/Router"; +import { WorkerScript } from "../Netscript/WorkerScript"; +import { HacknetServer } from "../Hacknet/HacknetServer"; export interface IPlayer { // Class members @@ -33,14 +35,12 @@ export interface IPlayer { bitNodeN: number; city: CityName; companyName: string; - corporation: ICorporation; - gang: IGang; - bladeburner: IBladeburner; + corporation: ICorporation | null; + gang: IGang | null; + bladeburner: IBladeburner | null; currentServer: string; factions: string[]; factionInvitations: string[]; - firstProgramAvailable: boolean; - firstTimeTraveled: boolean; hacknetNodes: (HacknetNode | string)[]; // HacknetNode object or IP of Hacknet Server has4SData: boolean; has4SDataTixApi: boolean; @@ -122,9 +122,13 @@ export interface IPlayer { bladeburner_analysis_mult: number; bladeburner_success_chance_mult: number; + createProgramReqLvl: number; + factionWorkType: string; createProgramName: string; timeWorkedCreateProgram: number; crimeType: string; + committingCrimeThruSingFn: boolean; + singFnCrimeWorkerScript: WorkerScript | null; timeNeededToCompleteWork: number; focus: boolean; className: string; @@ -151,20 +155,23 @@ export interface IPlayer { workMoneyLossRate: number; // Methods - applyForAgentJob(sing?: boolean): boolean | void; - applyForBusinessConsultantJob(sing?: boolean): boolean | void; - applyForBusinessJob(sing?: boolean): boolean | void; - applyForEmployeeJob(sing?: boolean): boolean | void; - applyForItJob(sing?: boolean): boolean | void; - applyForJob(entryPosType: CompanyPosition, sing?: boolean): boolean | void; - applyForNetworkEngineerJob(sing?: boolean): boolean | void; - applyForPartTimeEmployeeJob(sing?: boolean): boolean | void; - applyForPartTimeWaiterJob(sing?: boolean): boolean | void; - applyForSecurityEngineerJob(sing?: boolean): boolean | void; - applyForSecurityJob(sing?: boolean): boolean | void; - applyForSoftwareConsultantJob(sing?: boolean): boolean | void; - applyForSoftwareJob(sing?: boolean): boolean | void; - applyForWaiterJob(sing?: boolean): boolean | void; + work(numCycles: number): boolean; + workPartTime(numCycles: number): boolean; + workForFaction(numCycles: number): boolean; + applyForAgentJob(sing?: boolean): boolean; + applyForBusinessConsultantJob(sing?: boolean): boolean; + applyForBusinessJob(sing?: boolean): boolean; + applyForEmployeeJob(sing?: boolean): boolean; + applyForItJob(sing?: boolean): boolean; + applyForJob(entryPosType: CompanyPosition, sing?: boolean): boolean; + applyForNetworkEngineerJob(sing?: boolean): boolean; + applyForPartTimeEmployeeJob(sing?: boolean): boolean; + applyForPartTimeWaiterJob(sing?: boolean): boolean; + applyForSecurityEngineerJob(sing?: boolean): boolean; + applyForSecurityJob(sing?: boolean): boolean; + applyForSoftwareConsultantJob(sing?: boolean): boolean; + applyForSoftwareJob(sing?: boolean): boolean; + applyForWaiterJob(sing?: boolean): boolean; canAccessBladeburner(): boolean; canAccessCorporation(): boolean; canAccessGang(): boolean; @@ -178,11 +185,11 @@ export interface IPlayer { gainCharismaExp(exp: number): void; gainIntelligenceExp(exp: number): void; gainMoney(money: number): void; - getCurrentServer(): Server; + getCurrentServer(): Server | HacknetServer; getGangFaction(): Faction; getGangName(): string; getHomeComputer(): Server; - getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition; + getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition | null; getUpgradeHomeRamCost(): number; gotoLocation(to: LocationName): boolean; hasAugmentation(aug: Augmentation): boolean; @@ -201,6 +208,7 @@ export interface IPlayer { setMoney(amt: number): void; singularityStopWork(): void; startBladeburner(p: any): void; + startFactionWork(router: IRouter, faction: Faction): void; startClass(router: IRouter, costMult: number, expMult: number, className: string): void; startCorporation(corpName: string, additionalShares?: number): void; startCrime( @@ -237,11 +245,31 @@ export interface IPlayer { updateSkillLevels(): void; gainCodingContractReward(reward: ICodingContractReward, difficulty?: number): string; stopFocusing(): void; - finishFactionWork(cancelled: boolean, sing?: boolean): void; - finishClass(sing?: boolean): void; - finishWork(cancelled: boolean, sing?: boolean): void; + finishFactionWork(cancelled: boolean, sing?: boolean): string; + finishClass(sing?: boolean): string; + finishWork(cancelled: boolean, sing?: boolean): string; cancelationPenalty(): number; - finishWorkPartTime(sing?: boolean): void; - finishCrime(cancelled: boolean): void; - finishCreateProgramWork(cancelled: boolean): void; + finishWorkPartTime(sing?: boolean): string; + finishCrime(cancelled: boolean): string; + finishCreateProgramWork(cancelled: boolean): string; + resetMultipliers(): void; + prestigeAugmentation(): void; + prestigeSourceFile(): void; + calculateSkill(exp: number, mult?: number): number; + resetWorkStatus(generalType?: string, group?: string, workType?: string): void; + getWorkHackExpGain(): number; + getWorkStrExpGain(): number; + getWorkDefExpGain(): number; + getWorkDexExpGain(): number; + getWorkAgiExpGain(): number; + getWorkChaExpGain(): number; + getWorkRepGain(): number; + getWorkMoneyGain(): number; + processWorkEarnings(cycles: number): void; + hospitalize(): void; + createProgramWork(numCycles: number): boolean; + takeClass(numCycles: number): boolean; + commitCrime(numCycles: number): boolean; + checkForFactionInvitations(): void; + setBitNodeNumber(n: number): void; } diff --git a/src/PersonObjects/Player/PlayerObject.js b/src/PersonObjects/Player/PlayerObject.js deleted file mode 100644 index 32022fa3c..000000000 --- a/src/PersonObjects/Player/PlayerObject.js +++ /dev/null @@ -1,224 +0,0 @@ -import * as augmentationMethods from "./PlayerObjectAugmentationMethods"; -import * as bladeburnerMethods from "./PlayerObjectBladeburnerMethods"; -import * as corporationMethods from "./PlayerObjectCorporationMethods"; -import * as gangMethods from "./PlayerObjectGangMethods"; -import * as generalMethods from "./PlayerObjectGeneralMethods"; -import * as serverMethods from "./PlayerObjectServerMethods"; - -import { HashManager } from "../../Hacknet/HashManager"; -import { CityName } from "../../Locations/data/CityNames"; - -import { MoneySourceTracker } from "../../utils/MoneySourceTracker"; -import { Reviver, Generic_toJSON, Generic_fromJSON } from "../../../utils/JSONReviver"; - -import Decimal from "decimal.js"; - -export function PlayerObject() { - //Skills and stats - this.hacking_skill = 1; - - //Combat stats - this.hp = 10; - this.max_hp = 10; - this.strength = 1; - this.defense = 1; - this.dexterity = 1; - this.agility = 1; - - //Labor stats - this.charisma = 1; - - //Special stats - this.intelligence = 0; - - //Hacking multipliers - this.hacking_chance_mult = 1; - this.hacking_speed_mult = 1; - this.hacking_money_mult = 1; - this.hacking_grow_mult = 1; - - //Experience and multipliers - this.hacking_exp = 0; - this.strength_exp = 0; - this.defense_exp = 0; - this.dexterity_exp = 0; - this.agility_exp = 0; - this.charisma_exp = 0; - this.intelligence_exp = 0; - - this.hacking_mult = 1; - this.strength_mult = 1; - this.defense_mult = 1; - this.dexterity_mult = 1; - this.agility_mult = 1; - this.charisma_mult = 1; - - this.hacking_exp_mult = 1; - this.strength_exp_mult = 1; - this.defense_exp_mult = 1; - this.dexterity_exp_mult = 1; - this.agility_exp_mult = 1; - this.charisma_exp_mult = 1; - - this.company_rep_mult = 1; - this.faction_rep_mult = 1; - - //Money - this.money = new Decimal(1000); - - //IP Address of Starting (home) computer - this.homeComputer = ""; - - //Location information - this.city = CityName.Sector12; - this.location = ""; - - // Jobs that the player holds - // Map of company name (key) -> name of company position (value. Just the name, not the CompanyPosition object) - // The CompanyPosition name must match a key value in CompanyPositions - this.jobs = {}; - - // Company at which player is CURRENTLY working (only valid when the player is actively working) - this.companyName = ""; // Name of Company. Must match a key value in Companies map - - // Servers - this.currentServer = ""; //IP address of Server currently being accessed through terminal - this.purchasedServers = []; //IP Addresses of purchased servers - - // Hacknet Nodes/Servers - this.hacknetNodes = []; // Note: For Hacknet Servers, this array holds the IP addresses of the servers - this.hashManager = new HashManager(); - - //Factions - this.factions = []; //Names of all factions player has joined - this.factionInvitations = []; //Outstanding faction invitations - - //Augmentations - this.queuedAugmentations = []; - this.augmentations = []; - - this.sourceFiles = []; - - //Crime statistics - this.numPeopleKilled = 0; - this.karma = 0; - - this.crime_money_mult = 1; - this.crime_success_mult = 1; - - //Flags/variables for working (Company, Faction, Creating Program, Taking Class) - this.isWorking = false; - this.focus = false; - this.workType = ""; - - this.currentWorkFactionName = ""; - this.currentWorkFactionDescription = ""; - - this.workHackExpGainRate = 0; - this.workStrExpGainRate = 0; - this.workDefExpGainRate = 0; - this.workDexExpGainRate = 0; - this.workAgiExpGainRate = 0; - this.workChaExpGainRate = 0; - this.workRepGainRate = 0; - this.workMoneyGainRate = 0; - this.workMoneyLossRate = 0; - - this.workHackExpGained = 0; - this.workStrExpGained = 0; - this.workDefExpGained = 0; - this.workDexExpGained = 0; - this.workAgiExpGained = 0; - this.workChaExpGained = 0; - this.workRepGained = 0; - this.workMoneyGained = 0; - - this.createProgramName = ""; - this.createProgramReqLvl = 0; - - this.className = ""; - - this.crimeType = ""; - - this.timeWorked = 0; //in ms - this.timeWorkedCreateProgram = 0; - this.timeNeededToCompleteWork = 0; - - this.work_money_mult = 1; - - //Hacknet Node multipliers - this.hacknet_node_money_mult = 1; - this.hacknet_node_purchase_cost_mult = 1; - this.hacknet_node_ram_cost_mult = 1; - this.hacknet_node_core_cost_mult = 1; - this.hacknet_node_level_cost_mult = 1; - - //Stock Market - this.hasWseAccount = false; - this.hasTixApiAccess = false; - this.has4SData = false; - this.has4SDataTixApi = false; - - //Gang - this.gang = null; - - //Corporation - this.corporation = null; - - //Bladeburner - this.bladeburner = null; - this.bladeburner_max_stamina_mult = 1; - this.bladeburner_stamina_gain_mult = 1; - this.bladeburner_analysis_mult = 1; //Field Analysis Only - this.bladeburner_success_chance_mult = 1; - - // Sleeves & Re-sleeving - this.sleeves = []; - this.resleeves = []; - this.sleevesFromCovenant = 0; // # of Duplicate sleeves purchased from the covenant - - //bitnode - this.bitNodeN = 1; - - //Flags for determining whether certain "thresholds" have been achieved - this.firstFacInvRecvd = false; - this.firstAugPurchased = false; - this.firstTimeTraveled = false; - this.firstProgramAvailable = false; - - //Used to store the last update time. - this.lastUpdate = 0; - this.totalPlaytime = 0; - this.playtimeSinceLastAug = 0; - this.playtimeSinceLastBitnode = 0; - - // Keep track of where money comes from - this.moneySourceA = new MoneySourceTracker(); // Where money comes from since last-installed Augmentation - this.moneySourceB = new MoneySourceTracker(); // Where money comes from for this entire BitNode run - - // Production since last Augmentation installation - this.scriptProdSinceLastAug = 0; - - this.exploits = []; -} - -// Apply player methods to the prototype using Object.assign() -Object.assign( - PlayerObject.prototype, - generalMethods, - serverMethods, - bladeburnerMethods, - corporationMethods, - gangMethods, - augmentationMethods, -); - -PlayerObject.prototype.toJSON = function () { - return Generic_toJSON("PlayerObject", this); -}; - -PlayerObject.fromJSON = function (value) { - return Generic_fromJSON(PlayerObject, value.data); -}; - -Reviver.constructors.PlayerObject = PlayerObject; diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts new file mode 100644 index 000000000..ad7da4a70 --- /dev/null +++ b/src/PersonObjects/Player/PlayerObject.ts @@ -0,0 +1,591 @@ +import * as augmentationMethods from "./PlayerObjectAugmentationMethods"; +import * as bladeburnerMethods from "./PlayerObjectBladeburnerMethods"; +import * as corporationMethods from "./PlayerObjectCorporationMethods"; +import * as gangMethods from "./PlayerObjectGangMethods"; +import * as generalMethods from "./PlayerObjectGeneralMethods"; +import * as serverMethods from "./PlayerObjectServerMethods"; + +import { IMap } from "../../types"; +import { Resleeve } from "../Resleeving/Resleeve"; +import { Sleeve } from "../Sleeve/Sleeve"; +import { IPlayerOwnedSourceFile } from "../../SourceFile/PlayerOwnedSourceFile"; +import { Exploit } from "../../Exploits/Exploit"; +import { WorkerScript } from "../../Netscript/WorkerScript"; +import { CompanyPosition } from "../../Company/CompanyPosition"; +import { Server } from "../../Server/Server"; +import { HacknetServer } from "../../Hacknet/HacknetServer"; +import { Faction } from "../../Faction/Faction"; +import { Company } from "../../Company/Company"; +import { Augmentation } from "../../Augmentation/Augmentation"; +import { IRouter } from "../../ui/Router"; +import { ICodingContractReward } from "../../CodingContracts"; + +import { IPlayer } from "../IPlayer"; +import { LocationName } from "../../Locations/data/LocationNames"; +import { IPlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation"; +import { ICorporation } from "../../Corporation/ICorporation"; +import { IGang } from "../../Gang/IGang"; +import { IBladeburner } from "../../Bladeburner/IBladeburner"; +import { HacknetNode } from "../../Hacknet/HacknetNode"; + +import { HashManager } from "../../Hacknet/HashManager"; +import { CityName } from "../../Locations/data/CityNames"; + +import { MoneySourceTracker } from "../../utils/MoneySourceTracker"; +import { Reviver, Generic_toJSON, Generic_fromJSON } from "../../../utils/JSONReviver"; + +import Decimal from "decimal.js"; + +export class PlayerObject implements IPlayer { + // Class members + augmentations: IPlayerOwnedAugmentation[]; + bitNodeN: number; + city: CityName; + companyName: string; + corporation: ICorporation | null; + gang: IGang | null; + bladeburner: IBladeburner | null; + currentServer: string; + factions: string[]; + factionInvitations: string[]; + hacknetNodes: (HacknetNode | string)[]; // HacknetNode object or IP of Hacknet Server + has4SData: boolean; + has4SDataTixApi: boolean; + hashManager: HashManager; + hasTixApiAccess: boolean; + hasWseAccount: boolean; + homeComputer: string; + hp: number; + jobs: IMap; + init: () => void; + isWorking: boolean; + karma: number; + numPeopleKilled: number; + location: LocationName; + max_hp: number; + money: any; + moneySourceA: MoneySourceTracker; + moneySourceB: MoneySourceTracker; + playtimeSinceLastAug: number; + playtimeSinceLastBitnode: number; + purchasedServers: any[]; + queuedAugmentations: IPlayerOwnedAugmentation[]; + resleeves: Resleeve[]; + scriptProdSinceLastAug: number; + sleeves: Sleeve[]; + sleevesFromCovenant: number; + sourceFiles: IPlayerOwnedSourceFile[]; + exploits: Exploit[]; + lastUpdate: number; + totalPlaytime: number; + + // Stats + hacking_skill: number; + strength: number; + defense: number; + dexterity: number; + agility: number; + charisma: number; + intelligence: number; + + // Experience + hacking_exp: number; + strength_exp: number; + defense_exp: number; + dexterity_exp: number; + agility_exp: number; + charisma_exp: number; + intelligence_exp: number; + + // Multipliers + hacking_chance_mult: number; + hacking_speed_mult: number; + hacking_money_mult: number; + hacking_grow_mult: number; + hacking_mult: number; + hacking_exp_mult: number; + strength_mult: number; + strength_exp_mult: number; + defense_mult: number; + defense_exp_mult: number; + dexterity_mult: number; + dexterity_exp_mult: number; + agility_mult: number; + agility_exp_mult: number; + charisma_mult: number; + charisma_exp_mult: number; + hacknet_node_money_mult: number; + hacknet_node_purchase_cost_mult: number; + hacknet_node_ram_cost_mult: number; + hacknet_node_core_cost_mult: number; + hacknet_node_level_cost_mult: number; + company_rep_mult: number; + faction_rep_mult: number; + work_money_mult: number; + crime_success_mult: number; + crime_money_mult: number; + bladeburner_max_stamina_mult: number; + bladeburner_stamina_gain_mult: number; + bladeburner_analysis_mult: number; + bladeburner_success_chance_mult: number; + + createProgramReqLvl: number; + factionWorkType: string; + createProgramName: string; + timeWorkedCreateProgram: number; + crimeType: string; + committingCrimeThruSingFn: boolean; + singFnCrimeWorkerScript: WorkerScript | null; + timeNeededToCompleteWork: number; + focus: boolean; + className: string; + currentWorkFactionName: string; + workType: string; + currentWorkFactionDescription: string; + timeWorked: number; + workMoneyGained: number; + workMoneyGainRate: number; + workRepGained: number; + workRepGainRate: number; + workHackExpGained: number; + workHackExpGainRate: number; + workStrExpGained: number; + workStrExpGainRate: number; + workDefExpGained: number; + workDefExpGainRate: number; + workDexExpGained: number; + workDexExpGainRate: number; + workAgiExpGained: number; + workAgiExpGainRate: number; + workChaExpGained: number; + workChaExpGainRate: number; + workMoneyLossRate: number; + + // Methods + work: (numCycles: number) => boolean; + workPartTime: (numCycles: number) => boolean; + workForFaction: (numCycles: number) => boolean; + applyForAgentJob: (sing?: boolean) => boolean; + applyForBusinessConsultantJob: (sing?: boolean) => boolean; + applyForBusinessJob: (sing?: boolean) => boolean; + applyForEmployeeJob: (sing?: boolean) => boolean; + applyForItJob: (sing?: boolean) => boolean; + applyForJob: (entryPosType: CompanyPosition, sing?: boolean) => boolean; + applyForNetworkEngineerJob: (sing?: boolean) => boolean; + applyForPartTimeEmployeeJob: (sing?: boolean) => boolean; + applyForPartTimeWaiterJob: (sing?: boolean) => boolean; + applyForSecurityEngineerJob: (sing?: boolean) => boolean; + applyForSecurityJob: (sing?: boolean) => boolean; + applyForSoftwareConsultantJob: (sing?: boolean) => boolean; + applyForSoftwareJob: (sing?: boolean) => boolean; + applyForWaiterJob: (sing?: boolean) => boolean; + canAccessBladeburner: () => boolean; + canAccessCorporation: () => boolean; + canAccessGang: () => boolean; + canAccessResleeving: () => boolean; + canAfford: (cost: number) => boolean; + gainHackingExp: (exp: number) => void; + gainStrengthExp: (exp: number) => void; + gainDefenseExp: (exp: number) => void; + gainDexterityExp: (exp: number) => void; + gainAgilityExp: (exp: number) => void; + gainCharismaExp: (exp: number) => void; + gainIntelligenceExp: (exp: number) => void; + gainMoney: (money: number) => void; + getCurrentServer: () => Server | HacknetServer; + getGangFaction: () => Faction; + getGangName: () => string; + getHomeComputer: () => Server; + getNextCompanyPosition: (company: Company, entryPosType: CompanyPosition) => CompanyPosition | null; + getUpgradeHomeRamCost: () => number; + gotoLocation: (to: LocationName) => boolean; + hasAugmentation: (aug: Augmentation) => boolean; + hasCorporation: () => boolean; + hasGangWith: (facName: string) => boolean; + hasTorRouter: () => boolean; + hasProgram: (program: string) => boolean; + inBladeburner: () => boolean; + inGang: () => boolean; + isQualified: (company: Company, position: CompanyPosition) => boolean; + loseMoney: (money: number) => void; + reapplyAllAugmentations: (resetMultipliers: boolean) => void; + reapplyAllSourceFiles: () => void; + regenerateHp: (amt: number) => void; + recordMoneySource: (amt: number, source: string) => void; + setMoney: (amt: number) => void; + singularityStopWork: () => void; + startBladeburner: (p: any) => void; + startFactionWork: (router: IRouter, faction: Faction) => void; + startClass: (router: IRouter, costMult: number, expMult: number, className: string) => void; + startCorporation: (corpName: string, additionalShares?: number) => void; + startCrime: ( + router: IRouter, + crimeType: string, + hackExp: number, + strExp: number, + defExp: number, + dexExp: number, + agiExp: number, + chaExp: number, + money: number, + time: number, + singParams: any, + ) => void; + startFactionFieldWork: (router: IRouter, faction: Faction) => void; + startFactionHackWork: (router: IRouter, faction: Faction) => void; + startFactionSecurityWork: (router: IRouter, faction: Faction) => void; + startFocusing: () => void; + startGang: (facName: string, isHacking: boolean) => void; + startWork: (router: IRouter, companyName: string) => void; + startWorkPartTime: (router: IRouter, companyName: string) => void; + takeDamage: (amt: number) => boolean; + travel: (to: CityName) => boolean; + giveExploit: (exploit: Exploit) => void; + queryStatFromString: (str: string) => number; + getIntelligenceBonus: (weight: number) => number; + getCasinoWinnings: () => number; + quitJob: (company: string) => void; + createHacknetServer: () => void; + startCreateProgramWork: (router: IRouter, programName: string, time: number, reqLevel: number) => void; + queueAugmentation: (augmentationName: string) => void; + receiveInvite: (factionName: string) => void; + updateSkillLevels: () => void; + gainCodingContractReward: (reward: ICodingContractReward, difficulty?: number) => string; + stopFocusing: () => void; + finishFactionWork: (cancelled: boolean, sing?: boolean) => string; + finishClass: (sing?: boolean) => string; + finishWork: (cancelled: boolean, sing?: boolean) => string; + cancelationPenalty: () => number; + finishWorkPartTime: (sing?: boolean) => string; + finishCrime: (cancelled: boolean) => string; + finishCreateProgramWork: (cancelled: boolean) => string; + resetMultipliers: () => void; + prestigeAugmentation: () => void; + prestigeSourceFile: () => void; + calculateSkill: (exp: number, mult?: number) => number; + resetWorkStatus: (generalType?: string, group?: string, workType?: string) => void; + getWorkHackExpGain: () => number; + getWorkStrExpGain: () => number; + getWorkDefExpGain: () => number; + getWorkDexExpGain: () => number; + getWorkAgiExpGain: () => number; + getWorkChaExpGain: () => number; + getWorkRepGain: () => number; + getWorkMoneyGain: () => number; + processWorkEarnings: (cycles: number) => void; + hospitalize: () => void; + createProgramWork: (numCycles: number) => boolean; + takeClass: (numCycles: number) => boolean; + commitCrime: (numCycles: number) => boolean; + checkForFactionInvitations: () => void; + setBitNodeNumber: (n: number) => void; + + constructor() { + //Skills and stats + this.hacking_skill = 1; + + //Combat stats + this.hp = 10; + this.max_hp = 10; + this.strength = 1; + this.defense = 1; + this.dexterity = 1; + this.agility = 1; + + //Labor stats + this.charisma = 1; + + //Special stats + this.intelligence = 0; + + //Hacking multipliers + this.hacking_chance_mult = 1; + this.hacking_speed_mult = 1; + this.hacking_money_mult = 1; + this.hacking_grow_mult = 1; + + //Experience and multipliers + this.hacking_exp = 0; + this.strength_exp = 0; + this.defense_exp = 0; + this.dexterity_exp = 0; + this.agility_exp = 0; + this.charisma_exp = 0; + this.intelligence_exp = 0; + + this.hacking_mult = 1; + this.strength_mult = 1; + this.defense_mult = 1; + this.dexterity_mult = 1; + this.agility_mult = 1; + this.charisma_mult = 1; + + this.hacking_exp_mult = 1; + this.strength_exp_mult = 1; + this.defense_exp_mult = 1; + this.dexterity_exp_mult = 1; + this.agility_exp_mult = 1; + this.charisma_exp_mult = 1; + + this.company_rep_mult = 1; + this.faction_rep_mult = 1; + + //Money + this.money = new Decimal(1000); + + //IP Address of Starting (home) computer + this.homeComputer = ""; + + //Location information + this.city = CityName.Sector12; + this.location = LocationName.TravelAgency; + + // Jobs that the player holds + // Map of company name (key) -> name of company position (value. Just the name, not the CompanyPosition object) + // The CompanyPosition name must match a key value in CompanyPositions + this.jobs = {}; + + // Company at which player is CURRENTLY working (only valid when the player is actively working) + this.companyName = ""; // Name of Company. Must match a key value in Companies ma; + + // Servers + this.currentServer = ""; //IP address of Server currently being accessed through termina; + this.purchasedServers = []; //IP Addresses of purchased server; + + // Hacknet Nodes/Servers + this.hacknetNodes = []; // Note= For Hacknet Servers, this array holds the IP addresses of the server; + this.hashManager = new HashManager(); + + //Factions + this.factions = []; //Names of all factions player has joine; + this.factionInvitations = []; //Outstanding faction invitation; + + //Augmentations + this.queuedAugmentations = []; + this.augmentations = []; + + this.sourceFiles = []; + + //Crime statistics + this.numPeopleKilled = 0; + this.karma = 0; + + this.crime_money_mult = 1; + this.crime_success_mult = 1; + + //Flags/variables for working (Company, Faction, Creating Program, Taking Class) + this.isWorking = false; + this.focus = false; + this.workType = ""; + + this.currentWorkFactionName = ""; + this.currentWorkFactionDescription = ""; + + this.workHackExpGainRate = 0; + this.workStrExpGainRate = 0; + this.workDefExpGainRate = 0; + this.workDexExpGainRate = 0; + this.workAgiExpGainRate = 0; + this.workChaExpGainRate = 0; + this.workRepGainRate = 0; + this.workMoneyGainRate = 0; + this.workMoneyLossRate = 0; + + this.workHackExpGained = 0; + this.workStrExpGained = 0; + this.workDefExpGained = 0; + this.workDexExpGained = 0; + this.workAgiExpGained = 0; + this.workChaExpGained = 0; + this.workRepGained = 0; + this.workMoneyGained = 0; + + this.createProgramName = ""; + this.createProgramReqLvl = 0; + + this.className = ""; + + this.crimeType = ""; + + (this.timeWorked = 0), //in m; + (this.timeWorkedCreateProgram = 0); + this.timeNeededToCompleteWork = 0; + + this.work_money_mult = 1; + + //Hacknet Node multipliers + this.hacknet_node_money_mult = 1; + this.hacknet_node_purchase_cost_mult = 1; + this.hacknet_node_ram_cost_mult = 1; + this.hacknet_node_core_cost_mult = 1; + this.hacknet_node_level_cost_mult = 1; + + //Stock Market + this.hasWseAccount = false; + this.hasTixApiAccess = false; + this.has4SData = false; + this.has4SDataTixApi = false; + + //Gang + this.gang = null; + + //Corporation + this.corporation = null; + + //Bladeburner + this.bladeburner = null; + this.bladeburner_max_stamina_mult = 1; + this.bladeburner_stamina_gain_mult = 1; + (this.bladeburner_analysis_mult = 1), //Field Analysis Onl; + (this.bladeburner_success_chance_mult = 1); + + // Sleeves & Re-sleeving + this.sleeves = []; + this.resleeves = []; + (this.sleevesFromCovenant = 0), // # of Duplicate sleeves purchased from the covenan; + //bitnode + (this.bitNodeN = 1); + + //Used to store the last update time. + this.lastUpdate = 0; + this.totalPlaytime = 0; + this.playtimeSinceLastAug = 0; + this.playtimeSinceLastBitnode = 0; + + // Keep track of where money comes from + (this.moneySourceA = new MoneySourceTracker()), // Where money comes from since last-installed Augmentatio; + (this.moneySourceB = new MoneySourceTracker()), // Where money comes from for this entire BitNode ru; + // Production since last Augmentation installation + (this.scriptProdSinceLastAug = 0); + + this.exploits = []; + + this.init = generalMethods.init; + this.prestigeAugmentation = generalMethods.prestigeAugmentation; + this.prestigeSourceFile = generalMethods.prestigeSourceFile; + this.receiveInvite = generalMethods.receiveInvite; + this.calculateSkill = generalMethods.calculateSkill; + this.updateSkillLevels = generalMethods.updateSkillLevels; + this.resetMultipliers = generalMethods.resetMultipliers; + this.hasProgram = generalMethods.hasProgram; + this.setMoney = generalMethods.setMoney; + this.gainMoney = generalMethods.gainMoney; + this.loseMoney = generalMethods.loseMoney; + this.canAfford = generalMethods.canAfford; + this.recordMoneySource = generalMethods.recordMoneySource; + this.gainHackingExp = generalMethods.gainHackingExp; + this.gainStrengthExp = generalMethods.gainStrengthExp; + this.gainDefenseExp = generalMethods.gainDefenseExp; + this.gainDexterityExp = generalMethods.gainDexterityExp; + this.gainAgilityExp = generalMethods.gainAgilityExp; + this.gainCharismaExp = generalMethods.gainCharismaExp; + this.gainIntelligenceExp = generalMethods.gainIntelligenceExp; + this.queryStatFromString = generalMethods.queryStatFromString; + this.resetWorkStatus = generalMethods.resetWorkStatus; + this.processWorkEarnings = generalMethods.processWorkEarnings; + this.startWork = generalMethods.startWork; + this.cancelationPenalty = generalMethods.cancelationPenalty; + this.work = generalMethods.work; + this.finishWork = generalMethods.finishWork; + this.startWorkPartTime = generalMethods.startWorkPartTime; + this.workPartTime = generalMethods.workPartTime; + this.finishWorkPartTime = generalMethods.finishWorkPartTime; + this.startFocusing = generalMethods.startFocusing; + this.stopFocusing = generalMethods.stopFocusing; + this.startFactionWork = generalMethods.startFactionWork; + this.startFactionHackWork = generalMethods.startFactionHackWork; + this.startFactionFieldWork = generalMethods.startFactionFieldWork; + this.startFactionSecurityWork = generalMethods.startFactionSecurityWork; + this.workForFaction = generalMethods.workForFaction; + this.finishFactionWork = generalMethods.finishFactionWork; + this.getWorkMoneyGain = generalMethods.getWorkMoneyGain; + this.getWorkHackExpGain = generalMethods.getWorkHackExpGain; + this.getWorkStrExpGain = generalMethods.getWorkStrExpGain; + this.getWorkDefExpGain = generalMethods.getWorkDefExpGain; + this.getWorkDexExpGain = generalMethods.getWorkDexExpGain; + this.getWorkAgiExpGain = generalMethods.getWorkAgiExpGain; + this.getWorkChaExpGain = generalMethods.getWorkChaExpGain; + this.getWorkRepGain = generalMethods.getWorkRepGain; + this.startCreateProgramWork = generalMethods.startCreateProgramWork; + this.createProgramWork = generalMethods.createProgramWork; + this.finishCreateProgramWork = generalMethods.finishCreateProgramWork; + this.startClass = generalMethods.startClass; + this.takeClass = generalMethods.takeClass; + this.finishClass = generalMethods.finishClass; + this.startCrime = generalMethods.startCrime; + this.commitCrime = generalMethods.commitCrime; + this.finishCrime = generalMethods.finishCrime; + this.singularityStopWork = generalMethods.singularityStopWork; + this.takeDamage = generalMethods.takeDamage; + this.regenerateHp = generalMethods.regenerateHp; + this.hospitalize = generalMethods.hospitalize; + this.applyForJob = generalMethods.applyForJob; + this.getNextCompanyPosition = generalMethods.getNextCompanyPosition; + this.quitJob = generalMethods.quitJob; + this.applyForSoftwareJob = generalMethods.applyForSoftwareJob; + this.applyForSoftwareConsultantJob = generalMethods.applyForSoftwareConsultantJob; + this.applyForItJob = generalMethods.applyForItJob; + this.applyForSecurityEngineerJob = generalMethods.applyForSecurityEngineerJob; + this.applyForNetworkEngineerJob = generalMethods.applyForNetworkEngineerJob; + this.applyForBusinessJob = generalMethods.applyForBusinessJob; + this.applyForBusinessConsultantJob = generalMethods.applyForBusinessConsultantJob; + this.applyForSecurityJob = generalMethods.applyForSecurityJob; + this.applyForAgentJob = generalMethods.applyForAgentJob; + this.applyForEmployeeJob = generalMethods.applyForEmployeeJob; + this.applyForPartTimeEmployeeJob = generalMethods.applyForPartTimeEmployeeJob; + this.applyForWaiterJob = generalMethods.applyForWaiterJob; + this.applyForPartTimeWaiterJob = generalMethods.applyForPartTimeWaiterJob; + this.isQualified = generalMethods.isQualified; + this.reapplyAllAugmentations = generalMethods.reapplyAllAugmentations; + this.reapplyAllSourceFiles = generalMethods.reapplyAllSourceFiles; + this.checkForFactionInvitations = generalMethods.checkForFactionInvitations; + this.setBitNodeNumber = generalMethods.setBitNodeNumber; + this.queueAugmentation = generalMethods.queueAugmentation; + this.gainCodingContractReward = generalMethods.gainCodingContractReward; + this.travel = generalMethods.travel; + this.gotoLocation = generalMethods.gotoLocation; + this.canAccessResleeving = generalMethods.canAccessResleeving; + this.giveExploit = generalMethods.giveExploit; + this.getIntelligenceBonus = generalMethods.getIntelligenceBonus; + this.getCasinoWinnings = generalMethods.getCasinoWinnings; + this.hasAugmentation = augmentationMethods.hasAugmentation; + this.canAccessBladeburner = bladeburnerMethods.canAccessBladeburner; + this.inBladeburner = bladeburnerMethods.inBladeburner; + this.startBladeburner = bladeburnerMethods.startBladeburner; + this.canAccessCorporation = corporationMethods.canAccessCorporation; + this.hasCorporation = corporationMethods.hasCorporation; + this.startCorporation = corporationMethods.startCorporation; + this.canAccessGang = gangMethods.canAccessGang; + this.getGangFaction = gangMethods.getGangFaction; + this.getGangName = gangMethods.getGangName; + this.hasGangWith = gangMethods.hasGangWith; + this.inGang = gangMethods.inGang; + this.startGang = gangMethods.startGang; + + this.hasTorRouter = serverMethods.hasTorRouter; + this.getCurrentServer = serverMethods.getCurrentServer; + this.getHomeComputer = serverMethods.getHomeComputer; + this.getUpgradeHomeRamCost = serverMethods.getUpgradeHomeRamCost; + this.createHacknetServer = serverMethods.createHacknetServer; + this.factionWorkType = ""; + this.committingCrimeThruSingFn = false; + this.singFnCrimeWorkerScript = null; + } + + /** + * Serialize the current object to a JSON save state. + */ + toJSON(): any { + return Generic_toJSON("PlayerObject", this); + } + + /** + * Initiatizes a PlayerObject object from a JSON save state. + */ + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + static fromJSON(value: any): PlayerObject { + return Generic_fromJSON(PlayerObject, value.data); + } +} + +Reviver.constructors.PlayerObject = PlayerObject; diff --git a/src/PersonObjects/Player/PlayerObjectGangMethods.ts b/src/PersonObjects/Player/PlayerObjectGangMethods.ts index b8aea304f..c24783512 100644 --- a/src/PersonObjects/Player/PlayerObjectGangMethods.ts +++ b/src/PersonObjects/Player/PlayerObjectGangMethods.ts @@ -20,20 +20,34 @@ export function canAccessGang(this: IPlayer): boolean { } export function getGangFaction(this: IPlayer): Faction { - const fac = Factions[this.gang.facName]; + const gang = this.gang; + if (gang === null) { + throw new Error("Cannot get gang faction because player is not in a gang."); + } + const fac = Factions[gang.facName]; if (fac == null) { - throw new Error(`Gang has invalid faction name: ${this.gang.facName}`); + throw new Error(`Gang has invalid faction name: ${gang.facName}`); } return fac; } export function getGangName(this: IPlayer): string { - return this.inGang() ? this.gang.facName : ""; + if (!this.inGang()) return ""; + const gang = this.gang; + if (gang === null) { + throw new Error("Cannot get gang faction because player is not in a gang."); + } + return gang.facName; } export function hasGangWith(this: IPlayer, facName: string): boolean { - return this.inGang() && this.gang.facName === facName; + if (!this.inGang()) return false; + const gang = this.gang; + if (gang === null) { + throw new Error("Cannot get gang faction because player is not in a gang."); + } + return gang.facName === facName; } export function inGang(this: IPlayer): boolean { diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx similarity index 84% rename from src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx rename to src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index 872bb633b..b788202a7 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -1,3 +1,4 @@ +import { IPlayer } from "../IPlayer"; import { Augmentations } from "../../Augmentation/Augmentations"; import { applyAugmentation } from "../../Augmentation/AugmentationHelpers"; import { PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation"; @@ -9,11 +10,14 @@ import { Companies } from "../../Company/Companies"; import { getNextCompanyPositionHelper } from "../../Company/GetNextCompanyPosition"; import { getJobRequirementText } from "../../Company/GetJobRequirementText"; import { CompanyPositions } from "../../Company/CompanyPositions"; +import { CompanyPosition } from "../../Company/CompanyPosition"; import * as posNames from "../../Company/data/companypositionnames"; import { CONSTANTS } from "../../Constants"; import { Programs } from "../../Programs/Programs"; import { determineCrimeSuccess } from "../../Crime/CrimeHelpers"; +import { ICodingContractReward } from "../../CodingContracts"; import { Crimes } from "../../Crime/Crimes"; +import { Exploit } from "../../Exploits/Exploit"; import { Faction } from "../../Faction/Faction"; import { Factions } from "../../Faction/Factions"; import { resetGangs } from "../../Gang/AllGangs"; @@ -31,6 +35,7 @@ import { getFactionFieldWorkRepGain, } from "../formulas/reputation"; import { AllServers, AddToAllServers, createUniqueRandomIp } from "../../Server/AllServers"; +import { Server } from "../../Server/Server"; import { safetlyCreateUniqueServer } from "../../Server/ServerHelpers"; import { Settings } from "../../Settings/Settings"; import { SpecialServerIps, SpecialServerNames } from "../../Server/SpecialServerIps"; @@ -40,10 +45,12 @@ import { SourceFiles } from "../../SourceFile/SourceFiles"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { influenceStockThroughCompanyWork } from "../../StockMarket/PlayerInfluencing"; import { getHospitalizationCost } from "../../Hospital/Hospital"; +import { WorkerScript } from "../../Netscript/WorkerScript"; import Decimal from "decimal.js"; import { numeralWrapper } from "../../ui/numeralFormat"; +import { IRouter } from "../../ui/Router"; import { MoneySourceTracker } from "../../utils/MoneySourceTracker"; import { dialogBoxCreate } from "../../../utils/DialogBox"; import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; @@ -53,7 +60,7 @@ import { Money } from "../../ui/React/Money"; import React from "react"; -export function init() { +export function init(this: IPlayer) { /* Initialize Player's home computer */ var t_homeComp = safetlyCreateUniqueServer({ adminRights: true, @@ -71,7 +78,7 @@ export function init() { this.getHomeComputer().programs.push(Programs.NukeProgram.name); } -export function prestigeAugmentation() { +export function prestigeAugmentation(this: IPlayer) { var homeComp = this.getHomeComputer(); this.currentServer = homeComp.ip; this.homeComputer = homeComp.ip; @@ -99,7 +106,7 @@ export function prestigeAugmentation() { this.money = new Decimal(1000); this.city = CityName.Sector12; - this.location = ""; + this.location = LocationName.TravelAgency; this.companyName = ""; this.jobs = {}; @@ -113,7 +120,7 @@ export function prestigeAugmentation() { this.resleeves = []; - let numSleeves = Math.min(3, SourceFileFlags[10] + (this.bitNodeN === 10 ? 1 : 0)) + this.sleevesFromCovenant; + const numSleeves = Math.min(3, SourceFileFlags[10] + (this.bitNodeN === 10 ? 1 : 0)) + this.sleevesFromCovenant; if (this.sleeves.length > numSleeves) this.sleeves.length = numSleeves; for (let i = this.sleeves.length; i < numSleeves; i++) { this.sleeves.push(new Sleeve(this)); @@ -171,7 +178,7 @@ export function prestigeAugmentation() { this.hp = this.max_hp; } -export function prestigeSourceFile() { +export function prestigeSourceFile(this: IPlayer) { this.prestigeAugmentation(); // Duplicate sleeves are reset to level 1 every Bit Node (but the number of sleeves you have persists) for (let i = 0; i < this.sleeves.length; ++i) { @@ -202,27 +209,26 @@ export function prestigeSourceFile() { this.has4SDataTixApi = false; // BitNode 3: Corporatocracy - this.corporation = 0; + this.corporation = null; this.moneySourceB.reset(); this.playtimeSinceLastBitnode = 0; this.augmentations = []; } -export function receiveInvite(factionName) { +export function receiveInvite(this: IPlayer, factionName: string): void { if (this.factionInvitations.includes(factionName) || this.factions.includes(factionName)) { return; } - this.firstFacInvRecvd = true; this.factionInvitations.push(factionName); } //Calculates skill level based on experience. The same formula will be used for every skill -export function calculateSkill(exp, mult = 1) { +export function calculateSkill(this: IPlayer, exp: number, mult = 1): number { return calculateSkillF(exp, mult); } -export function updateSkillLevels() { +export function updateSkillLevels(this: IPlayer): void { this.hacking_skill = Math.max( 1, Math.floor(this.calculateSkill(this.hacking_exp, this.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier)), @@ -261,7 +267,7 @@ export function updateSkillLevels() { this.hp = Math.round(this.max_hp * ratio); } -export function resetMultipliers() { +export function resetMultipliers(this: IPlayer): void { this.hacking_chance_mult = 1; this.hacking_speed_mult = 1; this.hacking_money_mult = 1; @@ -301,7 +307,7 @@ export function resetMultipliers() { this.bladeburner_success_chance_mult = 1; } -export function hasProgram(programName) { +export function hasProgram(this: IPlayer, programName: string): boolean { const home = this.getHomeComputer(); if (home == null) { return false; @@ -315,7 +321,7 @@ export function hasProgram(programName) { return false; } -export function setMoney(money) { +export function setMoney(this: IPlayer, money: number): void { if (isNaN(money)) { console.error("NaN passed into Player.setMoney()"); return; @@ -323,7 +329,7 @@ export function setMoney(money) { this.money = new Decimal(money); } -export function gainMoney(money) { +export function gainMoney(this: IPlayer, money: number): void { if (isNaN(money)) { console.error("NaN passed into Player.gainMoney()"); return; @@ -331,7 +337,7 @@ export function gainMoney(money) { this.money = this.money.plus(money); } -export function loseMoney(money) { +export function loseMoney(this: IPlayer, money: number): void { if (isNaN(money)) { console.error("NaN passed into Player.loseMoney()"); return; @@ -340,7 +346,7 @@ export function loseMoney(money) { this.money = this.money.minus(money); } -export function canAfford(cost) { +export function canAfford(this: IPlayer, cost: number): boolean { if (isNaN(cost)) { console.error(`NaN passed into Player.canAfford()`); return false; @@ -348,7 +354,7 @@ export function canAfford(cost) { return this.money.gte(cost); } -export function recordMoneySource(amt, source) { +export function recordMoneySource(this: IPlayer, amt: number, source: string) { if (!(this.moneySourceA instanceof MoneySourceTracker)) { console.warn(`Player.moneySourceA was not properly initialized. Resetting`); this.moneySourceA = new MoneySourceTracker(); @@ -361,7 +367,7 @@ export function recordMoneySource(amt, source) { this.moneySourceB.record(amt, source); } -export function gainHackingExp(exp) { +export function gainHackingExp(this: IPlayer, exp: number) { if (isNaN(exp)) { console.error("ERR: NaN passed into Player.gainHackingExp()"); return; @@ -374,7 +380,7 @@ export function gainHackingExp(exp) { this.hacking_skill = calculateSkillF(this.hacking_exp, this.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier); } -export function gainStrengthExp(exp) { +export function gainStrengthExp(this: IPlayer, exp: number) { if (isNaN(exp)) { console.error("ERR: NaN passed into Player.gainStrengthExp()"); return; @@ -387,7 +393,7 @@ export function gainStrengthExp(exp) { this.strength = calculateSkillF(this.strength_exp, this.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier); } -export function gainDefenseExp(exp) { +export function gainDefenseExp(this: IPlayer, exp: number) { if (isNaN(exp)) { console.error("ERR: NaN passed into player.gainDefenseExp()"); return; @@ -400,7 +406,7 @@ export function gainDefenseExp(exp) { this.defense = calculateSkillF(this.defense_exp, this.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier); } -export function gainDexterityExp(exp) { +export function gainDexterityExp(this: IPlayer, exp: number) { if (isNaN(exp)) { console.error("ERR: NaN passed into Player.gainDexterityExp()"); return; @@ -416,7 +422,7 @@ export function gainDexterityExp(exp) { ); } -export function gainAgilityExp(exp) { +export function gainAgilityExp(this: IPlayer, exp: number): void { if (isNaN(exp)) { console.error("ERR: NaN passed into Player.gainAgilityExp()"); return; @@ -429,7 +435,7 @@ export function gainAgilityExp(exp) { this.agility = calculateSkillF(this.agility_exp, this.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier); } -export function gainCharismaExp(exp) { +export function gainCharismaExp(this: IPlayer, exp: number): void { if (isNaN(exp)) { console.error("ERR: NaN passed into Player.gainCharismaExp()"); return; @@ -442,7 +448,7 @@ export function gainCharismaExp(exp) { this.charisma = calculateSkillF(this.charisma_exp, this.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier); } -export function gainIntelligenceExp(exp) { +export function gainIntelligenceExp(this: IPlayer, exp: number): void { if (isNaN(exp)) { console.error("ERROR: NaN passed into Player.gainIntelligenceExp()"); return; @@ -453,7 +459,7 @@ export function gainIntelligenceExp(exp) { } //Given a string expression like "str" or "strength", returns the given stat -export function queryStatFromString(str) { +export function queryStatFromString(this: IPlayer, str: string): number { const tempStr = str.toLowerCase(); if (tempStr.includes("hack")) { return this.hacking_skill; @@ -476,10 +482,11 @@ export function queryStatFromString(str) { if (tempStr.includes("int")) { return this.intelligence; } + return 0; } /******* Working functions *******/ -export function resetWorkStatus(generalType, group, workType) { +export function resetWorkStatus(this: IPlayer, generalType?: string, group?: string, workType?: string): void { if (this.workType !== CONSTANTS.WorkTypeFaction && generalType === this.workType && group === this.companyName) return; if (generalType === this.workType && group === this.currentWorkFactionName && workType === this.factionWorkType) @@ -513,7 +520,7 @@ export function resetWorkStatus(generalType, group, workType) { this.className = ""; } -export function processWorkEarnings(numCycles = 1) { +export function processWorkEarnings(this: IPlayer, numCycles = 1) { const focusBonus = this.focus ? 1 : 0.8; const hackExpGain = focusBonus * this.workHackExpGainRate * numCycles; const strExpGain = focusBonus * this.workStrExpGainRate * numCycles; @@ -547,7 +554,7 @@ export function processWorkEarnings(numCycles = 1) { } /* Working for Company */ -export function startWork(router, companyName) { +export function startWork(this: IPlayer, router: IRouter, companyName: string): void { this.resetWorkStatus(CONSTANTS.WorkTypeCompany, companyName); this.isWorking = true; this.focus = true; @@ -567,16 +574,18 @@ export function startWork(router, companyName) { router.toWork(); } -export function cancelationPenalty() { +export function cancelationPenalty(this: IPlayer) { const specialIp = SpecialServerIps[this.companyName]; - if (specialIp) { + if (typeof specialIp === "string" && specialIp !== "") { const server = AllServers[specialIp]; - if (server && server.backdoorInstalled) return 0.75; + if (server instanceof Server) { + if (server && server.backdoorInstalled) return 0.75; + } } return 0.5; } -export function work(numCycles) { +export function work(this: IPlayer, numCycles: number): boolean { // Cap the number of cycles being processed to whatever would put you at // the work time limit (8 hours) var overMax = false; @@ -594,13 +603,13 @@ export function work(numCycles) { // If timeWorked == 8 hours, then finish. You can only gain 8 hours worth of exp and money if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) { - return this.finishWork(false); + this.finishWork(false); return true; } return false; } -export function finishWork(cancelled, sing = false) { +export function finishWork(this: IPlayer, cancelled: boolean, sing = false): string { //Since the work was cancelled early, player only gains half of what they've earned so far if (cancelled) { this.workRepGained *= this.cancelationPenalty(); @@ -652,8 +661,9 @@ export function finishWork(cancelled, sing = false) { this.isWorking = false; + this.resetWorkStatus(); if (sing) { - var res = + const res = "You worked a short shift of " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " + @@ -674,13 +684,14 @@ export function finishWork(cancelled, sing = false) { " agility exp, and " + numeralWrapper.formatExp(this.workChaExpGained) + " charisma exp."; - this.resetWorkStatus(); + return res; } - this.resetWorkStatus(); + + return ""; } -export function startWorkPartTime(router, companyName) { +export function startWorkPartTime(this: IPlayer, router: IRouter, companyName: string): void { this.resetWorkStatus(CONSTANTS.WorkTypeCompanyPartTime, companyName); this.isWorking = true; this.focus = true; @@ -700,7 +711,7 @@ export function startWorkPartTime(router, companyName) { router.toWork(); } -export function workPartTime(numCycles) { +export function workPartTime(this: IPlayer, numCycles: number): boolean { //Cap the number of cycles being processed to whatever would put you at the //work time limit (8 hours) var overMax = false; @@ -715,13 +726,13 @@ export function workPartTime(numCycles) { //If timeWorked == 8 hours, then finish. You can only gain 8 hours worth of exp and money if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) { - return this.finishWorkPartTime(); + this.finishWorkPartTime(); return true; } return false; } -export function finishWorkPartTime(sing = false) { +export function finishWorkPartTime(this: IPlayer, sing = false): string { var company = Companies[this.companyName]; company.playerReputation += this.workRepGained; @@ -750,6 +761,8 @@ export function finishWorkPartTime(sing = false) { } this.isWorking = false; + this.resetWorkStatus(); + if (sing) { var res = "You worked for " + @@ -773,22 +786,21 @@ export function finishWorkPartTime(sing = false) { " agility exp, and " + numeralWrapper.formatExp(this.workChaExpGained) + " charisma exp"; - this.resetWorkStatus(); return res; } - this.resetWorkStatus(); + return ""; } -export function startFocusing() { +export function startFocusing(this: IPlayer): void { this.focus = true; } -export function stopFocusing() { +export function stopFocusing(this: IPlayer): void { this.focus = false; } /* Working for Faction */ -export function startFactionWork(router, faction) { +export function startFactionWork(this: IPlayer, router: IRouter, faction: Faction): void { //Update reputation gain rate to account for faction favor var favorMult = 1 + faction.favor / 100; if (isNaN(favorMult)) { @@ -806,7 +818,7 @@ export function startFactionWork(router, faction) { router.toWork(); } -export function startFactionHackWork(router, faction) { +export function startFactionHackWork(this: IPlayer, router: IRouter, faction: Faction) { this.resetWorkStatus(CONSTANTS.WorkTypeFaction, faction.name, CONSTANTS.FactionWorkHacking); this.workHackExpGainRate = 0.15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain; @@ -821,7 +833,7 @@ export function startFactionHackWork(router, faction) { this.startFactionWork(router, faction); } -export function startFactionFieldWork(router, faction) { +export function startFactionFieldWork(this: IPlayer, router: IRouter, faction: Faction) { this.resetWorkStatus(CONSTANTS.WorkTypeFaction, faction.name, CONSTANTS.FactionWorkField); this.workHackExpGainRate = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain; @@ -838,7 +850,7 @@ export function startFactionFieldWork(router, faction) { this.startFactionWork(router, faction); } -export function startFactionSecurityWork(router, faction) { +export function startFactionSecurityWork(this: IPlayer, router: IRouter, faction: Faction) { this.resetWorkStatus(CONSTANTS.WorkTypeFaction, faction.name, CONSTANTS.FactionWorkSecurity); this.workHackExpGainRate = 0.05 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain; @@ -855,7 +867,7 @@ export function startFactionSecurityWork(router, faction) { this.startFactionWork(router, faction); } -export function workForFaction(numCycles) { +export function workForFaction(this: IPlayer, numCycles: number) { const faction = Factions[this.currentWorkFactionName]; //Constantly update the rep gain rate @@ -874,7 +886,7 @@ export function workForFaction(numCycles) { } //Cap the number of cycles being processed to whatever would put you at limit (20 hours) - var overMax = false; + let overMax = false; if (this.timeWorked + CONSTANTS._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer20Hours) { overMax = true; numCycles = Math.round((CONSTANTS.MillisecondsPer20Hours - this.timeWorked) / CONSTANTS._idleSpeed); @@ -891,8 +903,8 @@ export function workForFaction(numCycles) { return false; } -export function finishFactionWork(cancelled, sing = false) { - var faction = Factions[this.currentWorkFactionName]; +export function finishFactionWork(this: IPlayer, cancelled: boolean, sing = false): string { + const faction = Factions[this.currentWorkFactionName]; faction.playerReputation += this.workRepGained; this.updateSkillLevels(); @@ -919,7 +931,7 @@ export function finishFactionWork(cancelled, sing = false) { } this.isWorking = false; - + this.resetWorkStatus(); if (sing) { var res = "You worked for your faction " + @@ -942,14 +954,14 @@ export function finishFactionWork(cancelled, sing = false) { " agi exp, and " + numeralWrapper.formatExp(this.workChaExpGained) + " cha exp."; - this.resetWorkStatus(); + return res; } - this.resetWorkStatus(); + return ""; } //Money gained per game cycle -export function getWorkMoneyGain() { +export function getWorkMoneyGain(this: IPlayer): number { // If player has SF-11, calculate salary multiplier from favor let bn11Mult = 1; const company = Companies[this.companyName]; @@ -975,7 +987,7 @@ export function getWorkMoneyGain() { } //Hack exp gained per game cycle -export function getWorkHackExpGain() { +export function getWorkHackExpGain(this: IPlayer): number { const company = Companies[this.companyName]; const companyPositionName = this.jobs[this.companyName]; const companyPosition = CompanyPositions[companyPositionName]; @@ -999,7 +1011,7 @@ export function getWorkHackExpGain() { } //Str exp gained per game cycle -export function getWorkStrExpGain() { +export function getWorkStrExpGain(this: IPlayer): number { const company = Companies[this.companyName]; const companyPositionName = this.jobs[this.companyName]; const companyPosition = CompanyPositions[companyPositionName]; @@ -1023,7 +1035,7 @@ export function getWorkStrExpGain() { } //Def exp gained per game cycle -export function getWorkDefExpGain() { +export function getWorkDefExpGain(this: IPlayer): number { const company = Companies[this.companyName]; const companyPositionName = this.jobs[this.companyName]; const companyPosition = CompanyPositions[companyPositionName]; @@ -1047,7 +1059,7 @@ export function getWorkDefExpGain() { } //Dex exp gained per game cycle -export function getWorkDexExpGain() { +export function getWorkDexExpGain(this: IPlayer): number { const company = Companies[this.companyName]; const companyPositionName = this.jobs[this.companyName]; const companyPosition = CompanyPositions[companyPositionName]; @@ -1071,7 +1083,7 @@ export function getWorkDexExpGain() { } //Agi exp gained per game cycle -export function getWorkAgiExpGain() { +export function getWorkAgiExpGain(this: IPlayer): number { const company = Companies[this.companyName]; const companyPositionName = this.jobs[this.companyName]; const companyPosition = CompanyPositions[companyPositionName]; @@ -1095,7 +1107,7 @@ export function getWorkAgiExpGain() { } //Charisma exp gained per game cycle -export function getWorkChaExpGain() { +export function getWorkChaExpGain(this: IPlayer): number { const company = Companies[this.companyName]; const companyPositionName = this.jobs[this.companyName]; const companyPosition = CompanyPositions[companyPositionName]; @@ -1119,7 +1131,7 @@ export function getWorkChaExpGain() { } //Reputation gained per game cycle -export function getWorkRepGain() { +export function getWorkRepGain(this: IPlayer): number { const company = Companies[this.companyName]; const companyPositionName = this.jobs[this.companyName]; const companyPosition = CompanyPositions[companyPositionName]; @@ -1154,7 +1166,7 @@ export function getWorkRepGain() { return jobPerformance * this.company_rep_mult * favorMult; } -// export function getFactionSecurityWorkRepGain() { +// export function getFactionSecurityWorkRepGain(this: IPlayer) { // var t = 0.9 * (this.hacking_skill / CONSTANTS.MaxSkillLevel + // this.strength / CONSTANTS.MaxSkillLevel + // this.defense / CONSTANTS.MaxSkillLevel + @@ -1163,7 +1175,7 @@ export function getWorkRepGain() { // return t * this.faction_rep_mult; // } -// export function getFactionFieldWorkRepGain() { +// export function getFactionFieldWorkRepGain(this: IPlayer) { // var t = 0.9 * (this.hacking_skill / CONSTANTS.MaxSkillLevel + // this.strength / CONSTANTS.MaxSkillLevel + // this.defense / CONSTANTS.MaxSkillLevel + @@ -1175,7 +1187,13 @@ export function getWorkRepGain() { // } /* Creating a Program */ -export function startCreateProgramWork(router, programName, time, reqLevel) { +export function startCreateProgramWork( + this: IPlayer, + router: IRouter, + programName: string, + time: number, + reqLevel: number, +): void { this.resetWorkStatus(); this.isWorking = true; this.focus = true; @@ -1210,7 +1228,7 @@ export function startCreateProgramWork(router, programName, time, reqLevel) { router.toWork(); } -export function createProgramWork(numCycles) { +export function createProgramWork(this: IPlayer, numCycles: number): boolean { //Higher hacking skill will allow you to create programs faster var reqLvl = this.createProgramReqLvl; var skillMult = (this.hacking_skill / reqLvl) * this.getIntelligenceBonus(3); //This should always be greater than 1; @@ -1227,7 +1245,7 @@ export function createProgramWork(numCycles) { return false; } -export function finishCreateProgramWork(cancelled) { +export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): string { var programName = this.createProgramName; if (cancelled === false) { dialogBoxCreate( @@ -1248,10 +1266,11 @@ export function finishCreateProgramWork(cancelled) { this.isWorking = false; this.resetWorkStatus(); + return "You've finished creating " + programName + "! The new program can be found on your home computer."; } /* Studying/Taking Classes */ -export function startClass(router, costMult, expMult, className) { +export function startClass(this: IPlayer, router: IRouter, costMult: number, expMult: number, className: string): void { this.resetWorkStatus(); this.isWorking = true; this.focus = true; @@ -1325,7 +1344,7 @@ export function startClass(router, costMult, expMult, className) { router.toWork(); } -export function takeClass(numCycles) { +export function takeClass(this: IPlayer, numCycles: number): boolean { this.timeWorked += CONSTANTS._idleSpeed * numCycles; this.processWorkEarnings(numCycles); return false; @@ -1333,7 +1352,7 @@ export function takeClass(numCycles) { //The 'sing' argument defines whether or not this function was called //through a Singularity Netscript function -export function finishClass(sing = false) { +export function finishClass(this: IPlayer, sing = false): string { this.gainIntelligenceExp(CONSTANTS.IntelligenceClassBaseExpGain * Math.round(this.timeWorked / 1000)); if (this.workMoneyGained > 0) { @@ -1388,22 +1407,24 @@ export function finishClass(sing = false) { return res; } this.resetWorkStatus(); + return ""; } //The EXP and $ gains are hardcoded. Time is in ms export function startCrime( - router, - crimeType, - hackExp, - strExp, - defExp, - dexExp, - agiExp, - chaExp, - money, - time, - singParams = null, -) { + this: IPlayer, + router: IRouter, + crimeType: string, + hackExp: number, + strExp: number, + defExp: number, + dexExp: number, + agiExp: number, + chaExp: number, + money: number, + time: number, + workerscript: WorkerScript | null = null, +): void { this.crimeType = crimeType; this.resetWorkStatus(); @@ -1411,9 +1432,9 @@ export function startCrime( this.focus = true; this.workType = CONSTANTS.WorkTypeCrime; - if (singParams && singParams.workerscript) { + if (workerscript !== null) { this.committingCrimeThruSingFn = true; - this.singFnCrimeWorkerScript = singParams.workerscript; + this.singFnCrimeWorkerScript = workerscript; } this.workHackExpGained = hackExp * this.hacking_exp_mult * BitNodeMultipliers.CrimeExpGain; @@ -1428,7 +1449,7 @@ export function startCrime( router.toWork(); } -export function commitCrime(numCycles) { +export function commitCrime(this: IPlayer, numCycles: number): boolean { this.timeWorked += CONSTANTS._idleSpeed * numCycles; if (this.timeWorked >= this.timeNeededToCompleteWork) { @@ -1438,7 +1459,7 @@ export function commitCrime(numCycles) { return false; } -export function finishCrime(cancelled) { +export function finishCrime(this: IPlayer, cancelled: boolean): string { //Determine crime success/failure if (!cancelled) { if (determineCrimeSuccess(this, this.crimeType)) { @@ -1454,6 +1475,7 @@ export function finishCrime(cancelled) { dialogBoxCreate( `ERR: Unrecognized crime type (${this.crimeType}). This is probably a bug please contact the developer`, ); + return ""; } this.gainMoney(this.workMoneyGained); this.recordMoneySource(this.workMoneyGained, "crime"); @@ -1470,12 +1492,10 @@ export function finishCrime(cancelled) { this.workDexExpGained *= 2; this.workAgiExpGained *= 2; this.workChaExpGained *= 2; - if (this.committingCrimeThruSingFn) { - if ( - this.singFnCrimeWorkerScript.disableLogs.ALL == null && - this.singFnCrimeWorkerScript.disableLogs.commitCrime == null - ) { - this.singFnCrimeWorkerScript.scriptRef.log( + const ws = this.singFnCrimeWorkerScript; + if (this.committingCrimeThruSingFn && ws !== null) { + if (ws.disableLogs.ALL == null && ws.disableLogs.commitCrime == null) { + ws.scriptRef.log( "Crime successful! Gained " + numeralWrapper.formatMoney(this.workMoneyGained) + ", " + @@ -1524,12 +1544,10 @@ export function finishCrime(cancelled) { this.workDexExpGained /= 2; this.workAgiExpGained /= 2; this.workChaExpGained /= 2; - if (this.committingCrimeThruSingFn) { - if ( - this.singFnCrimeWorkerScript.disableLogs.ALL == null && - this.singFnCrimeWorkerScript.disableLogs.commitCrime == null - ) { - this.singFnCrimeWorkerScript.scriptRef.log( + const ws = this.singFnCrimeWorkerScript; + if (this.committingCrimeThruSingFn && ws !== null) { + if (ws.disableLogs.ALL == null && ws.disableLogs.commitCrime == null) { + ws.scriptRef.log( "Crime failed! Gained " + numeralWrapper.formatExp(this.workHackExpGained) + " hack exp, " + @@ -1580,15 +1598,16 @@ export function finishCrime(cancelled) { this.isWorking = false; this.crimeType = ""; this.resetWorkStatus(); + return ""; } //Cancels the player's current "work" assignment and gives the proper rewards //Used only for Singularity functions, so no popups are created -export function singularityStopWork() { +export function singularityStopWork(this: IPlayer): string { if (!this.isWorking) { return ""; } - var res; //Earnings text for work + let res = ""; //Earnings text for work switch (this.workType) { case CONSTANTS.WorkTypeStudyClass: res = this.finishClass(true); @@ -1616,10 +1635,10 @@ export function singularityStopWork() { } // Returns true if hospitalized, false otherwise -export function takeDamage(amt) { +export function takeDamage(this: IPlayer, amt: number): boolean { if (typeof amt !== "number") { console.warn(`Player.takeDamage() called without a numeric argument: ${amt}`); - return; + return false; } this.hp -= amt; @@ -1631,7 +1650,7 @@ export function takeDamage(amt) { } } -export function regenerateHp(amt) { +export function regenerateHp(this: IPlayer, amt: number): void { if (typeof amt !== "number") { console.warn(`Player.regenerateHp() called without a numeric argument: ${amt}`); return; @@ -1642,7 +1661,7 @@ export function regenerateHp(amt) { } } -export function hospitalize() { +export function hospitalize(this: IPlayer): number { const cost = getHospitalizationCost(this); if (Settings.SuppressHospitalizationPopup === false) { dialogBoxCreate( @@ -1664,7 +1683,7 @@ export function hospitalize() { //Determines the job that the Player should get (if any) at the current company //The 'sing' argument designates whether or not this is being called from //the applyToCompany() Netscript Singularity function -export function applyForJob(entryPosType, sing = false) { +export function applyForJob(this: IPlayer, entryPosType: CompanyPosition, sing = false): boolean { // Get current company and job let currCompany = null; if (this.companyName !== "") { @@ -1675,25 +1694,18 @@ export function applyForJob(entryPosType, sing = false) { // Get company that's being applied to const company = Companies[this.location]; //Company being applied to if (!(company instanceof Company)) { - if (sing) { - return "ERROR: Invalid company name: " + this.location + ". applyToCompany() failed"; - } else { - console.error( - `Could not find company that matches the location: ${this.location}. Player.applyToCompany() failed`, - ); - return; - } + console.error(`Could not find company that matches the location: ${this.location}. Player.applyToCompany() failed`); + return false; } let pos = entryPosType; if (!this.isQualified(company, pos)) { - var reqText = getJobRequirementText(company, pos); - if (sing) { - return false; + const reqText = getJobRequirementText(company, pos); + if (!sing) { + dialogBoxCreate("Unforunately, you do not qualify for this position
" + reqText); } - dialogBoxCreate("Unforunately, you do not qualify for this position
" + reqText); - return; + return false; } while (true) { @@ -1717,40 +1729,43 @@ export function applyForJob(entryPosType, sing = false) { //Check if the determined job is the same as the player's current job if (currCompany != null) { if (currCompany.name == company.name && pos.name == currPositionName) { - var nextPos = getNextCompanyPositionHelper(pos); + const nextPos = getNextCompanyPositionHelper(pos); if (nextPos == null) { - if (sing) { - return false; + if (!sing) { + dialogBoxCreate("You are already at the highest position for your field! No promotion available"); } - dialogBoxCreate("You are already at the highest position for your field! No promotion available"); + return false; } else if (company.hasPosition(nextPos)) { - if (sing) { - return false; + if (!sing) { + var reqText = getJobRequirementText(company, nextPos); + dialogBoxCreate("Unfortunately, you do not qualify for a promotion
" + reqText); } - var reqText = getJobRequirementText(company, nextPos); - dialogBoxCreate("Unfortunately, you do not qualify for a promotion
" + reqText); + return false; } else { - if (sing) { - return false; + if (!sing) { + dialogBoxCreate("You are already at the highest position for your field! No promotion available"); } - dialogBoxCreate("You are already at the highest position for your field! No promotion available"); + return false; } - return; //Same job, do nothing + return false; //Same job, do nothing } } this.jobs[company.name] = pos.name; this.companyName = this.location; - if (sing) { - return true; + if (!sing) { + dialogBoxCreate("Congratulations! You were offered a new job at " + this.companyName + " as a " + pos.name + "!"); } - - dialogBoxCreate("Congratulations! You were offered a new job at " + this.companyName + " as a " + pos.name + "!"); + return true; } //Returns your next position at a company given the field (software, business, etc.) -export function getNextCompanyPosition(company, entryPosType) { +export function getNextCompanyPosition( + this: IPlayer, + company: Company, + entryPosType: CompanyPosition, +): CompanyPosition | null { var currCompany = null; if (this.companyName !== "") { currCompany = Companies[this.companyName]; @@ -1784,143 +1799,149 @@ export function getNextCompanyPosition(company, entryPosType) { return entryPosType; } -export function quitJob(company) { +export function quitJob(this: IPlayer, company: string): void { this.isWorking = false; this.companyName = ""; delete this.jobs[company]; } -export function applyForSoftwareJob(sing = false) { +export function applyForSoftwareJob(this: IPlayer, sing = false): boolean { return this.applyForJob(CompanyPositions[posNames.SoftwareCompanyPositions[0]], sing); } -export function applyForSoftwareConsultantJob(sing = false) { +export function applyForSoftwareConsultantJob(this: IPlayer, sing = false): boolean { return this.applyForJob(CompanyPositions[posNames.SoftwareConsultantCompanyPositions[0]], sing); } -export function applyForItJob(sing = false) { +export function applyForItJob(this: IPlayer, sing = false): boolean { return this.applyForJob(CompanyPositions[posNames.ITCompanyPositions[0]], sing); } -export function applyForSecurityEngineerJob(sing = false) { +export function applyForSecurityEngineerJob(this: IPlayer, sing = false): boolean { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.SecurityEngineerCompanyPositions[0]])) { return this.applyForJob(CompanyPositions[posNames.SecurityEngineerCompanyPositions[0]], sing); } else { - if (sing) { - return false; + if (!sing) { + dialogBoxCreate("Unforunately, you do not qualify for this position"); } - dialogBoxCreate("Unforunately, you do not qualify for this position"); + return false; } } -export function applyForNetworkEngineerJob(sing = false) { +export function applyForNetworkEngineerJob(this: IPlayer, sing = false): boolean { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.NetworkEngineerCompanyPositions[0]])) { - return this.applyForJob(CompanyPositions[posNames.NetworkEngineerCompanyPositions[0]], sing); + const pos = CompanyPositions[posNames.NetworkEngineerCompanyPositions[0]]; + return this.applyForJob(pos, sing); } else { - if (sing) { - return false; + if (!sing) { + dialogBoxCreate("Unforunately, you do not qualify for this position"); } - dialogBoxCreate("Unforunately, you do not qualify for this position"); + return false; } } -export function applyForBusinessJob(sing = false) { +export function applyForBusinessJob(this: IPlayer, sing = false): boolean { return this.applyForJob(CompanyPositions[posNames.BusinessCompanyPositions[0]], sing); } -export function applyForBusinessConsultantJob(sing = false) { +export function applyForBusinessConsultantJob(this: IPlayer, sing = false): boolean { return this.applyForJob(CompanyPositions[posNames.BusinessConsultantCompanyPositions[0]], sing); } -export function applyForSecurityJob(sing = false) { +export function applyForSecurityJob(this: IPlayer, sing = false): boolean { // TODO Police Jobs // Indexing starts at 2 because 0 is for police officer return this.applyForJob(CompanyPositions[posNames.SecurityCompanyPositions[2]], sing); } -export function applyForAgentJob(sing = false) { +export function applyForAgentJob(this: IPlayer, sing = false): boolean { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.AgentCompanyPositions[0]])) { - return this.applyForJob(CompanyPositions[posNames.AgentCompanyPositions[0]], sing); + const pos = CompanyPositions[posNames.AgentCompanyPositions[0]]; + return this.applyForJob(pos, sing); } else { - if (sing) { - return false; + if (!sing) { + dialogBoxCreate("Unforunately, you do not qualify for this position"); } - dialogBoxCreate("Unforunately, you do not qualify for this position"); + return false; } } -export function applyForEmployeeJob(sing = false) { +export function applyForEmployeeJob(this: IPlayer, sing = false): boolean { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.MiscCompanyPositions[1]])) { this.companyName = company.name; this.jobs[company.name] = posNames.MiscCompanyPositions[1]; - if (sing) { - return true; + if (!sing) { + dialogBoxCreate("Congratulations, you are now employed at " + this.companyName); } - dialogBoxCreate("Congratulations, you are now employed at " + this.companyName); + + return true; } else { - if (sing) { - return false; + if (!sing) { + dialogBoxCreate("Unforunately, you do not qualify for this position"); } - dialogBoxCreate("Unforunately, you do not qualify for this position"); + + return false; } } -export function applyForPartTimeEmployeeJob(sing = false) { +export function applyForPartTimeEmployeeJob(this: IPlayer, sing = false): boolean { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[1]])) { this.jobs[company.name] = posNames.PartTimeCompanyPositions[1]; - if (sing) { - return true; + if (!sing) { + dialogBoxCreate("Congratulations, you are now employed part-time at " + this.companyName); } - dialogBoxCreate("Congratulations, you are now employed part-time at " + this.companyName); + + return true; } else { - if (sing) { - return false; + if (!sing) { + dialogBoxCreate("Unforunately, you do not qualify for this position"); } - dialogBoxCreate("Unforunately, you do not qualify for this position"); + + return false; } } -export function applyForWaiterJob(sing = false) { +export function applyForWaiterJob(this: IPlayer, sing = false): boolean { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.MiscCompanyPositions[0]])) { this.companyName = company.name; this.jobs[company.name] = posNames.MiscCompanyPositions[0]; - if (sing) { - return true; + if (!sing) { + dialogBoxCreate("Congratulations, you are now employed as a waiter at " + this.companyName); } - dialogBoxCreate("Congratulations, you are now employed as a waiter at " + this.companyName); + return true; } else { - if (sing) { - return false; + if (!sing) { + dialogBoxCreate("Unforunately, you do not qualify for this position"); } - dialogBoxCreate("Unforunately, you do not qualify for this position"); + return false; } } -export function applyForPartTimeWaiterJob(sing = false) { +export function applyForPartTimeWaiterJob(this: IPlayer, sing = false): boolean { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[0]])) { this.companyName = company.name; this.jobs[company.name] = posNames.PartTimeCompanyPositions[0]; - if (sing) { - return true; + if (!sing) { + dialogBoxCreate("Congratulations, you are now employed as a part-time waiter at " + this.companyName); } - dialogBoxCreate("Congratulations, you are now employed as a part-time waiter at " + this.companyName); + return true; } else { - if (sing) { - return false; + if (!sing) { + dialogBoxCreate("Unforunately, you do not qualify for this position"); } - dialogBoxCreate("Unforunately, you do not qualify for this position"); + return false; } } //Checks if the Player is qualified for a certain position -export function isQualified(company, position) { +export function isQualified(this: IPlayer, company: Company, position: CompanyPosition): boolean { var offset = company.jobStatReqOffset; var reqHacking = position.requiredHacking > 0 ? position.requiredHacking + offset : 0; var reqStrength = position.requiredStrength > 0 ? position.requiredStrength + offset : 0; @@ -1944,7 +1965,7 @@ export function isQualified(company, position) { } /********** Reapplying Augmentations and Source File ***********/ -export function reapplyAllAugmentations(resetMultipliers = true) { +export function reapplyAllAugmentations(this: IPlayer, resetMultipliers = true): void { if (resetMultipliers) { this.resetMultipliers(); } @@ -1974,7 +1995,7 @@ export function reapplyAllAugmentations(resetMultipliers = true) { this.updateSkillLevels(); } -export function reapplyAllSourceFiles() { +export function reapplyAllSourceFiles(this: IPlayer): void { //Will always be called after reapplyAllAugmentations() so multipliers do not have to be reset //this.resetMultipliers(); @@ -1994,7 +2015,7 @@ export function reapplyAllSourceFiles() { //This function sets the requirements to join a Faction. It checks whether the Player meets //those requirements and will return an array of all factions that the Player should //receive an invitation to -export function checkForFactionInvitations() { +export function checkForFactionInvitations(this: IPlayer) { let invitedFactions = []; //Array which will hold all Factions the player should be invited to var numAugmentations = this.augmentations.length; @@ -2003,7 +2024,7 @@ export function checkForFactionInvitations() { const allPositions = Object.values(this.jobs); // Given a company name, safely returns the reputation (returns 0 if invalid company is specified) - function getCompanyRep(companyName) { + function getCompanyRep(companyName: string): number { const company = Companies[companyName]; if (company == null) { return 0; @@ -2016,7 +2037,7 @@ export function checkForFactionInvitations() { // the requirements for the specified company. There are two requirements: // 1. High enough reputation // 2. Player is employed at the company - function checkMegacorpRequirements(companyName, repNeeded = CONSTANTS.CorpFactionRepRequirement) { + function checkMegacorpRequirements(companyName: string, repNeeded = CONSTANTS.CorpFactionRepRequirement): boolean { return allCompanies.includes(companyName) && getCompanyRep(companyName) > repNeeded; } @@ -2168,8 +2189,11 @@ export function checkForFactionInvitations() { } //Fulcrum Secret Technologies - If u've unlocked fulcrum secret technolgoies server and have a high rep with the company - var fulcrumsecrettechonologiesFac = Factions["Fulcrum Secret Technologies"]; - var fulcrumSecretServer = AllServers[SpecialServerIps[SpecialServerNames.FulcrumSecretTechnologies]]; + const fulcrumsecrettechonologiesFac = Factions["Fulcrum Secret Technologies"]; + const fulcrumIP = SpecialServerIps[SpecialServerNames.BitRunnersServer]; + if (typeof fulcrumIP !== "string") throw new Error("Fulcrum Secret Technologies should be string"); + const fulcrumSecretServer = AllServers[fulcrumIP]; + if (!(fulcrumSecretServer instanceof Server)) throw new Error("Fulcrum Secret Technologies should be normal server"); if (fulcrumSecretServer == null) { console.error("Could not find Fulcrum Secret Technologies Server"); } else { @@ -2185,8 +2209,11 @@ export function checkForFactionInvitations() { } //BitRunners - var bitrunnersFac = Factions["BitRunners"]; - var bitrunnersServer = AllServers[SpecialServerIps[SpecialServerNames.BitRunnersServer]]; + const bitrunnersFac = Factions["BitRunners"]; + const bitrunnerIP = SpecialServerIps[SpecialServerNames.BitRunnersServer]; + if (typeof bitrunnerIP !== "string") throw new Error("BitRunners should be string"); + const bitrunnersServer = AllServers[bitrunnerIP]; + if (!(bitrunnersServer instanceof Server)) throw new Error("BitRunners should be normal server"); if (bitrunnersServer == null) { console.error("Could not find BitRunners Server"); } else if ( @@ -2199,8 +2226,12 @@ export function checkForFactionInvitations() { } //The Black Hand - var theblackhandFac = Factions["The Black Hand"]; - var blackhandServer = AllServers[SpecialServerIps[SpecialServerNames.TheBlackHandServer]]; + + const theblackhandFac = Factions["The Black Hand"]; + const tbhIP = SpecialServerIps[SpecialServerNames.TheBlackHandServer]; + if (typeof tbhIP !== "string") throw new Error("TheBlackHand should be string"); + const blackhandServer = AllServers[tbhIP]; + if (!(blackhandServer instanceof Server)) throw new Error("TheBlackHand should be normal server"); if (blackhandServer == null) { console.error("Could not find The Black Hand Server"); } else if ( @@ -2213,8 +2244,11 @@ export function checkForFactionInvitations() { } //NiteSec - var nitesecFac = Factions["NiteSec"]; - var nitesecServer = AllServers[SpecialServerIps[SpecialServerNames.NiteSecServer]]; + const nitesecFac = Factions["NiteSec"]; + const nitesecIP = SpecialServerIps[SpecialServerNames.NiteSecServer]; + if (typeof nitesecIP !== "string") throw new Error("NiteSec should be string"); + const nitesecServer = AllServers[nitesecIP]; + if (!(nitesecServer instanceof Server)) throw new Error("NiteSec should be normal server"); if (nitesecServer == null) { console.error("Could not find NiteSec Server"); } else if ( @@ -2410,17 +2444,17 @@ export function checkForFactionInvitations() { var totalHacknetCores = 0; var totalHacknetLevels = 0; for (let i = 0; i < this.hacknetNodes.length; ++i) { - if (hasHacknetServers(this)) { - const hserver = AllServers[this.hacknetNodes[i]]; - if (hserver) { - totalHacknetLevels += hserver.level; - totalHacknetRam += hserver.maxRam; - totalHacknetCores += hserver.cores; - } + const v = this.hacknetNodes[i]; + if (typeof v === "string") { + const hserver = AllServers[v]; + if (hserver instanceof Server) throw new Error("player hacknet server was not HacknetServer"); + totalHacknetLevels += hserver.level; + totalHacknetRam += hserver.maxRam; + totalHacknetCores += hserver.cores; } else { - totalHacknetLevels += this.hacknetNodes[i].level; - totalHacknetRam += this.hacknetNodes[i].ram; - totalHacknetCores += this.hacknetNodes[i].cores; + totalHacknetLevels += v.level; + totalHacknetRam += v.ram; + totalHacknetCores += v.cores; } } if ( @@ -2449,8 +2483,11 @@ export function checkForFactionInvitations() { } //CyberSec - var cybersecFac = Factions["CyberSec"]; - var cybersecServer = AllServers[SpecialServerIps[SpecialServerNames.CyberSecServer]]; + const cybersecFac = Factions["CyberSec"]; + const cyberSecIP = SpecialServerIps[SpecialServerNames.CyberSecServer]; + if (typeof cyberSecIP !== "string") throw new Error("cybersec should be string"); + const cybersecServer = AllServers[cyberSecIP]; + if (!(cybersecServer instanceof Server)) throw new Error("cybersec should be normal server"); if (cybersecServer == null) { console.error("Could not find CyberSec Server"); } else if ( @@ -2466,11 +2503,11 @@ export function checkForFactionInvitations() { } /************* BitNodes **************/ -export function setBitNodeNumber(n) { +export function setBitNodeNumber(this: IPlayer, n: number) { this.bitNodeN = n; } -export function queueAugmentation(name) { +export function queueAugmentation(this: IPlayer, name: string) { for (const i in this.queuedAugmentations) { if (this.queuedAugmentations[i].name == name) { console.warn(`tried to queue ${name} twice, this may be a bug`); @@ -2485,12 +2522,11 @@ export function queueAugmentation(name) { } } - this.firstAugPurchased = true; this.queuedAugmentations.push(new PlayerOwnedAugmentation(name)); } /************* Coding Contracts **************/ -export function gainCodingContractReward(reward, difficulty = 1) { +export function gainCodingContractReward(this: IPlayer, reward: ICodingContractReward, difficulty = 1) { if (reward == null || reward.type == null || reward == null) { return `No reward for this contract`; } @@ -2552,7 +2588,7 @@ export function gainCodingContractReward(reward, difficulty = 1) { /* eslint-enable no-case-declarations */ } -export function travel(to) { +export function travel(this: IPlayer, to: CityName) { if (Cities[to] == null) { console.warn(`Player.travel() called with invalid city: ${to}`); return false; @@ -2562,7 +2598,7 @@ export function travel(to) { return true; } -export function gotoLocation(to) { +export function gotoLocation(this: IPlayer, to: LocationName) { if (Locations[to] == null) { console.warn(`Player.gotoLocation() called with invalid location: ${to}`); return false; @@ -2572,20 +2608,20 @@ export function gotoLocation(to) { return true; } -export function canAccessResleeving() { +export function canAccessResleeving(this: IPlayer) { return this.bitNodeN === 10 || SourceFileFlags[10] > 0; } -export function giveExploit(exploit) { +export function giveExploit(this: IPlayer, exploit: Exploit) { if (!this.exploits.includes(exploit)) { this.exploits.push(exploit); } } -export function getIntelligenceBonus(weight) { +export function getIntelligenceBonus(this: IPlayer, weight: number) { return calculateIntelligenceBonus(this.intelligence, weight); } -export function getCasinoWinnings() { +export function getCasinoWinnings(this: IPlayer) { return this.moneySourceA.casino; } diff --git a/src/PersonObjects/Player/PlayerObjectServerMethods.ts b/src/PersonObjects/Player/PlayerObjectServerMethods.ts index 2361fa6ef..b9bf0e60b 100644 --- a/src/PersonObjects/Player/PlayerObjectServerMethods.ts +++ b/src/PersonObjects/Player/PlayerObjectServerMethods.ts @@ -15,12 +15,16 @@ export function hasTorRouter(this: IPlayer): boolean { return SpecialServerIps.hasOwnProperty("Darkweb Server"); } -export function getCurrentServer(this: IPlayer): Server | HacknetServer | null { - return AllServers[this.currentServer]; +export function getCurrentServer(this: IPlayer): Server | HacknetServer { + const server = AllServers[this.currentServer]; + if (server === null) throw new Error("somehow connected to a server that does not exist."); + return server; } -export function getHomeComputer(this: IPlayer): Server | HacknetServer | null { - return AllServers[this.homeComputer]; +export function getHomeComputer(this: IPlayer): Server { + const home = AllServers[this.homeComputer]; + if (home instanceof Server) return home; + throw new Error("home computer was not a normal server"); } export function getUpgradeHomeRamCost(this: IPlayer): number { diff --git a/src/Player.d.ts b/src/Player.d.ts deleted file mode 100644 index a4e5b3f7a..000000000 --- a/src/Player.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { IPlayer } from "./PersonObjects/IPlayer"; - -export declare let Player: IPlayer; diff --git a/src/Player.js b/src/Player.ts similarity index 85% rename from src/Player.js rename to src/Player.ts index 79db73bfc..852d757c5 100644 --- a/src/Player.js +++ b/src/Player.ts @@ -8,7 +8,7 @@ import Decimal from "decimal.js"; export let Player = new PlayerObject(); -export function loadPlayer(saveString) { +export function loadPlayer(saveString: string): void { Player = JSON.parse(saveString, Reviver); // Parse Decimal.js objects @@ -19,8 +19,8 @@ export function loadPlayer(saveString) { Player.corporation.revenue = new Decimal(Player.corporation.revenue); Player.corporation.expenses = new Decimal(Player.corporation.expenses); - for (var i = 0; i < Player.corporation.divisions.length; ++i) { - var ind = Player.corporation.divisions[i]; + for (let i = 0; i < Player.corporation.divisions.length; ++i) { + const ind = Player.corporation.divisions[i]; ind.lastCycleRevenue = new Decimal(ind.lastCycleRevenue); ind.lastCycleExpenses = new Decimal(ind.lastCycleExpenses); ind.thisCycleRevenue = new Decimal(ind.thisCycleRevenue); diff --git a/src/Programs/data/ProgramsMetadata.ts b/src/Programs/data/ProgramsMetadata.ts index 45ca6b7dc..09ea12820 100644 --- a/src/Programs/data/ProgramsMetadata.ts +++ b/src/Programs/data/ProgramsMetadata.ts @@ -53,10 +53,9 @@ export const programsMetadata: IProgramCreationParams[] = [ terminal.print("You already have root access to this computer. There is no reason to run NUKE.exe"); return; } - - if (server.openPortCount >= player.getCurrentServer().numOpenPortsRequired) { + if (server.openPortCount >= server.numOpenPortsRequired) { server.hasAdminRights = true; - terminal.print("NUKE successful! Gained root access to " + player.getCurrentServer().hostname); + terminal.print("NUKE successful! Gained root access to " + server.hostname); // TODO: Make this take time rather than be instant return; } diff --git a/src/Script/RunningScript.ts b/src/Script/RunningScript.ts index 683b095be..acf9c0f52 100644 --- a/src/Script/RunningScript.ts +++ b/src/Script/RunningScript.ts @@ -3,7 +3,6 @@ * A Script can have multiple active instances */ import { Script } from "./Script"; -import { FconfSettings } from "../Fconf/FconfSettings"; import { Settings } from "../Settings/Settings"; import { IMap } from "../types"; import { Terminal } from "../Terminal"; diff --git a/src/ScriptEditor/ui/Root.tsx b/src/ScriptEditor/ui/Root.tsx index 5d6b0f53e..ec3fe83e9 100644 --- a/src/ScriptEditor/ui/Root.tsx +++ b/src/ScriptEditor/ui/Root.tsx @@ -11,7 +11,6 @@ import { isValidFilePath } from "../../Terminal/DirectoryHelpers"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { IRouter } from "../../ui/Router"; import { dialogBoxCreate } from "../../../utils/DialogBox"; -import { parseFconfSettings } from "../../Fconf/Fconf"; import { isScriptFilename } from "../../Script/ScriptHelpersTS"; import { Script } from "../../Script/Script"; import { TextFile } from "../../TextFile"; @@ -144,7 +143,7 @@ export function Root(props: IProps): React.ReactElement { return; } - if (filename !== ".fconf" && !isValidFilePath(filename)) { + if (!isValidFilePath(filename)) { dialogBoxCreate( "Script filename can contain only alphanumerics, hyphens, and underscores, and must end with an extension.", ); @@ -153,14 +152,7 @@ export function Root(props: IProps): React.ReactElement { const server = props.player.getCurrentServer(); if (server === null) throw new Error("Server should not be null but it is."); - if (filename === ".fconf") { - try { - parseFconfSettings(code); - } catch (e) { - dialogBoxCreate(`Invalid .fconf file: ${e}`); - return; - } - } else if (isScriptFilename(filename)) { + if (isScriptFilename(filename)) { //If the current script already exists on the server, overwrite it for (let i = 0; i < server.scripts.length; i++) { if (filename == server.scripts[i].filename) { diff --git a/src/Sidebar/ui/SidebarRoot.tsx b/src/Sidebar/ui/SidebarRoot.tsx index 4138a8522..af9986c05 100644 --- a/src/Sidebar/ui/SidebarRoot.tsx +++ b/src/Sidebar/ui/SidebarRoot.tsx @@ -295,7 +295,6 @@ export function SidebarRoot(props: IProps): React.ReactElement { event.preventDefault(); clickCreateProgram(); } else if (event.keyCode === KEY.F && event.altKey) { - // Overriden by Fconf if (props.page == Page.Terminal && Settings.EnableBashHotkeys) { return; } diff --git a/src/Terminal.d.ts b/src/Terminal.d.ts deleted file mode 100644 index 80b9d17aa..000000000 --- a/src/Terminal.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare const Terminal: ITerminal; diff --git a/src/Terminal.jsx b/src/Terminal.ts similarity index 100% rename from src/Terminal.jsx rename to src/Terminal.ts diff --git a/src/Terminal/Terminal.ts b/src/Terminal/Terminal.ts index 77a4140b6..45fa46b75 100644 --- a/src/Terminal/Terminal.ts +++ b/src/Terminal/Terminal.ts @@ -3,6 +3,7 @@ import { IRouter } from "../ui/Router"; import { IPlayer } from "../PersonObjects/IPlayer"; import { HacknetServer } from "../Hacknet/HacknetServer"; import { BaseServer } from "../Server/BaseServer"; +import { Server } from "../Server/Server"; import { Programs } from "../Programs/Programs"; import { CodingContractResult } from "../CodingContracts"; import { TerminalEvents, TerminalClearEvents } from "./TerminalEvents"; @@ -106,12 +107,22 @@ export class Terminal implements ITerminal { startHack(player: IPlayer): void { // Hacking through Terminal should be faster than hacking through a script - this.startAction(calculateHackingTime(player.getCurrentServer(), player) / 4, "h"); + const server = player.getCurrentServer(); + if (server instanceof HacknetServer) { + this.error("Cannot hack this kind of server"); + return; + } + this.startAction(calculateHackingTime(server, player) / 4, "h"); } startBackdoor(player: IPlayer): void { // Backdoor should take the same amount of time as hack - this.startAction(calculateHackingTime(player.getCurrentServer(), player) / 4, "b"); + const server = player.getCurrentServer(); + if (server instanceof HacknetServer) { + this.error("Cannot backdoor this kind of server"); + return; + } + this.startAction(calculateHackingTime(server, player) / 4, "b"); } startAnalyze(): void { @@ -127,6 +138,10 @@ export class Terminal implements ITerminal { finishHack(router: IRouter, player: IPlayer, cancelled = false): void { if (cancelled) return; const server = player.getCurrentServer(); + if (server instanceof HacknetServer) { + this.error("Cannot hack this kind of server"); + return; + } // Calculate whether hack was successful const hackChance = calculateHackingChance(server, player); @@ -179,6 +194,10 @@ export class Terminal implements ITerminal { finishBackdoor(router: IRouter, player: IPlayer, cancelled = false): void { if (!cancelled) { const server = player.getCurrentServer(); + if (server instanceof HacknetServer) { + this.error("Cannot hack this kind of server"); + return; + } if ( SpecialServerIps[SpecialServerNames.WorldDaemon] && SpecialServerIps[SpecialServerNames.WorldDaemon] == server.ip @@ -203,24 +222,30 @@ export class Terminal implements ITerminal { this.print("Organization name: " + (!isHacknet ? org : "player")); const hasAdminRights = (!isHacknet && currServ.hasAdminRights) || isHacknet; this.print("Root Access: " + (hasAdminRights ? "YES" : "NO")); - const hackingSkill = currServ.requiredHackingSkill; - this.print("Required hacking skill: " + (!isHacknet ? hackingSkill : "N/A")); - const security = currServ.hackDifficulty; - this.print("Server security level: " + (!isHacknet ? numeralWrapper.formatServerSecurity(security) : "N/A")); - const hackingChance = calculateHackingChance(currServ, player); - this.print("Chance to hack: " + (!isHacknet ? numeralWrapper.formatPercentage(hackingChance) : "N/A")); - const hackingTime = calculateHackingTime(currServ, player) * 1000; - this.print("Time to hack: " + (!isHacknet ? convertTimeMsToTimeElapsedString(hackingTime, true) : "N/A")); + if (currServ instanceof Server) { + const hackingSkill = currServ.requiredHackingSkill; + this.print("Required hacking skill: " + (!isHacknet ? hackingSkill : "N/A")); + const security = currServ.hackDifficulty; + this.print("Server security level: " + (!isHacknet ? numeralWrapper.formatServerSecurity(security) : "N/A")); + const hackingChance = calculateHackingChance(currServ, player); + this.print("Chance to hack: " + (!isHacknet ? numeralWrapper.formatPercentage(hackingChance) : "N/A")); + const hackingTime = calculateHackingTime(currServ, player) * 1000; + this.print("Time to hack: " + (!isHacknet ? convertTimeMsToTimeElapsedString(hackingTime, true) : "N/A")); + } this.print( - `Total money available on server: ${!isHacknet ? numeralWrapper.formatMoney(currServ.moneyAvailable) : "N/A"}`, + `Total money available on server: ${ + !(currServ instanceof HacknetServer) ? numeralWrapper.formatMoney(currServ.moneyAvailable) : "N/A" + }`, ); - const numPort = currServ.numOpenPortsRequired; - this.print("Required number of open ports for NUKE: " + (!isHacknet ? numPort : "N/A")); - this.print("SSH port: " + (currServ.sshPortOpen ? "Open" : "Closed")); - this.print("FTP port: " + (currServ.ftpPortOpen ? "Open" : "Closed")); - this.print("SMTP port: " + (currServ.smtpPortOpen ? "Open" : "Closed")); - this.print("HTTP port: " + (currServ.httpPortOpen ? "Open" : "Closed")); - this.print("SQL port: " + (currServ.sqlPortOpen ? "Open" : "Closed")); + if (currServ instanceof Server) { + const numPort = currServ.numOpenPortsRequired; + this.print("Required number of open ports for NUKE: " + (!isHacknet ? numPort : "N/A")); + this.print("SSH port: " + (currServ.sshPortOpen ? "Open" : "Closed")); + this.print("FTP port: " + (currServ.ftpPortOpen ? "Open" : "Closed")); + this.print("SMTP port: " + (currServ.smtpPortOpen ? "Open" : "Closed")); + this.print("HTTP port: " + (currServ.httpPortOpen ? "Open" : "Closed")); + this.print("SQL port: " + (currServ.sqlPortOpen ? "Open" : "Closed")); + } } } diff --git a/src/Terminal/commands/nano.ts b/src/Terminal/commands/nano.ts index ad93cf2f9..b028cf9ac 100644 --- a/src/Terminal/commands/nano.ts +++ b/src/Terminal/commands/nano.ts @@ -3,7 +3,6 @@ import { IRouter } from "../../ui/Router"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { BaseServer } from "../../Server/BaseServer"; import { isScriptFilename } from "../../Script/ScriptHelpersTS"; -import { createFconf } from "../../Fconf/Fconf"; export function nano( terminal: ITerminal, @@ -19,11 +18,7 @@ export function nano( try { const filename = args[0] + ""; - if (filename === ".fconf") { - const text = createFconf(); - router.toScriptEditor(filename, text); - return; - } else if (isScriptFilename(filename)) { + if (isScriptFilename(filename)) { const filepath = terminal.getFilepath(filename); const script = terminal.getScript(player, filename); if (script == null) { diff --git a/src/Terminal/ui/TerminalRoot.tsx b/src/Terminal/ui/TerminalRoot.tsx index e4dc0caf9..1351d20ee 100644 --- a/src/Terminal/ui/TerminalRoot.tsx +++ b/src/Terminal/ui/TerminalRoot.tsx @@ -12,6 +12,7 @@ import { IRouter } from "../../ui/Router"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { TerminalInput } from "./TerminalInput"; import { TerminalEvents, TerminalClearEvents } from "../TerminalEvents"; +import _ from "lodash"; interface IActionTimerProps { terminal: ITerminal; @@ -60,8 +61,8 @@ export function TerminalRoot({ terminal, router, player }: IProps): React.ReactE setKey((key) => key + 1); } - useEffect(() => TerminalEvents.subscribe(rerender), []); - useEffect(() => TerminalClearEvents.subscribe(clear), []); + useEffect(() => TerminalEvents.subscribe(_.debounce(rerender, 50, { maxWait: 50 })), []); + useEffect(() => TerminalClearEvents.subscribe(_.debounce(clear, 50, { maxWait: 50 })), []); function doScroll(): void { const hook = scrollHook.current; diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index 66539c6df..41ccc8e57 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -326,11 +326,11 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme ) : page === Page.DevMenu ? ( ) : page === Page.Gang ? ( - + ) : page === Page.Corporation ? ( - + ) : page === Page.Bladeburner ? ( - + ) : page === Page.Resleeves ? ( ) : page === Page.Travel ? ( diff --git a/src/ui/MainMenu/Headers.ts b/src/ui/MainMenu/Headers.ts deleted file mode 100644 index 5ba3b971e..000000000 --- a/src/ui/MainMenu/Headers.ts +++ /dev/null @@ -1,177 +0,0 @@ -// Implement the collapsible main menu headers -import { MainMenuLinks } from "./Links"; -import { IPlayer } from "../../PersonObjects/IPlayer"; - -interface IMainMenuHeaders { - Hacking: HTMLElement | null; - Character: HTMLElement | null; - World: HTMLElement | null; - Help: HTMLElement | null; -} - -export const MainMenuHeaders: IMainMenuHeaders = { - Hacking: null, - Character: null, - World: null, - Help: null, -}; - -// Implements collapsible toggle feature when a header is clicked -function toggleHeader(open: boolean, elems: HTMLElement[], links: HTMLElement[]): void { - for (let i = 0; i < elems.length; ++i) { - if (open) { - elems[i].style.opacity = "1"; - elems[i].style.maxHeight = elems[i].scrollHeight + "px"; - } else { - elems[i].style.opacity = "0"; - elems[i].style.maxHeight = ""; - } - } - - for (let i = 0; i < links.length; ++i) { - if (open) { - links[i].style.opacity = "1"; - links[i].style.maxHeight = links[i].scrollHeight + "px"; - links[i].style.pointerEvents = "auto"; - } else { - links[i].style.opacity = "0"; - links[i].style.maxHeight = ""; - links[i].style.pointerEvents = "none"; - } - } -} - -export function initializeMainMenuHeaders(p: IPlayer, dev = false): boolean { - function safeGetElement(id: string): HTMLElement { - const elem: HTMLElement | null = document.getElementById(id); - if (elem == null) { - throw new Error(`Failed to find element with id ${id} in initializeMainMenuHeaders()`); - } - - return elem; - } - - try { - // Get references to the DOM elements - MainMenuHeaders.Hacking = safeGetElement("hacking-menu-header"); - MainMenuHeaders.Character = safeGetElement("character-menu-header"); - MainMenuHeaders.World = safeGetElement("world-menu-header"); - MainMenuHeaders.Help = safeGetElement("help-menu-header"); - - // Set click handlers to turn the headers into collapsibles - MainMenuHeaders.Hacking.onclick = function () { - const terminal: HTMLElement = safeGetElement("terminal-tab"); - const createScript: HTMLElement = safeGetElement("create-script-tab"); - const activeScripts: HTMLElement = safeGetElement("active-scripts-tab"); - const createProgram: HTMLElement = safeGetElement("create-program-tab"); - const createProgramNot: HTMLElement = safeGetElement("create-program-notification"); - - createProgram.style.display = p.firstProgramAvailable ? "list-item" : "none"; - - (this as any).classList.toggle("opened"); - - const elems: HTMLElement[] = [terminal, createScript, activeScripts, createProgram]; - const links: HTMLElement[] = [ - MainMenuLinks.Terminal, - MainMenuLinks.ScriptEditor, - MainMenuLinks.ActiveScripts, - MainMenuLinks.CreateProgram, - ]; - if (terminal.style.maxHeight) { - toggleHeader(false, elems, links); - createProgramNot.style.display = "none"; - } else { - toggleHeader(true, elems, links); - createProgramNot.style.display = "block"; - } - }; - - MainMenuHeaders.Character.onclick = function () { - const stats: HTMLElement = safeGetElement("stats-tab"); - const factions: HTMLElement = safeGetElement("factions-tab"); - const augmentations: HTMLElement = safeGetElement("augmentations-tab"); - const hacknetnodes: HTMLElement = safeGetElement("hacknet-nodes-tab"); - const sleeves: HTMLElement = safeGetElement("sleeves-tab"); - - sleeves.style.display = p.sleeves.length > 0 ? "list-item" : "none"; - - (this as any).classList.toggle("opened"); - - const elems: HTMLElement[] = [stats, factions, augmentations, hacknetnodes, sleeves]; - const links: HTMLElement[] = [ - MainMenuLinks.Stats, - MainMenuLinks.Factions, - MainMenuLinks.Augmentations, - MainMenuLinks.HacknetNodes, - MainMenuLinks.Sleeves, - ]; - if (stats.style.maxHeight) { - toggleHeader(false, elems, links); - } else { - toggleHeader(true, elems, links); - } - }; - - MainMenuHeaders.World.onclick = function () { - const city: HTMLElement = safeGetElement("city-tab"); - const travel: HTMLElement = safeGetElement("travel-tab"); - const job: HTMLElement = safeGetElement("job-tab"); - const stockmarket: HTMLElement = safeGetElement("stock-market-tab"); - const bladeburner: HTMLElement = safeGetElement("bladeburner-tab"); - const corporation: HTMLElement = safeGetElement("corporation-tab"); - const gang: HTMLElement = safeGetElement("gang-tab"); - - // Determine whether certain links should show up - job.style.display = p.companyName !== "" ? "list-item" : "none"; - stockmarket.style.display = p.hasWseAccount ? "list-item" : "none"; - bladeburner.style.display = p.inBladeburner() ? "list-item" : "none"; - corporation.style.display = p.hasCorporation() ? "list-item" : "none"; - gang.style.display = p.inGang() ? "list-item" : "none"; - - (this as any).classList.toggle("opened"); - - const elems: HTMLElement[] = [city, travel, job, stockmarket, bladeburner, corporation, gang]; - const links: HTMLElement[] = [ - MainMenuLinks.City, - MainMenuLinks.Travel, - MainMenuLinks.Job, - MainMenuLinks.StockMarket, - MainMenuLinks.Bladeburner, - MainMenuLinks.Corporation, - MainMenuLinks.Gang, - ]; - if (city.style.maxHeight) { - toggleHeader(false, elems, links); - } else { - toggleHeader(true, elems, links); - } - }; - - MainMenuHeaders.Help.onclick = function () { - const milestones: HTMLElement = safeGetElement("milestones-tab"); - const tutorial: HTMLElement = safeGetElement("tutorial-tab"); - const options: HTMLElement = safeGetElement("options-tab"); - - (this as any).classList.toggle("opened"); - - const elems: HTMLElement[] = [milestones, tutorial, options]; - const links: HTMLElement[] = [MainMenuLinks.Milestones, MainMenuLinks.Tutorial, MainMenuLinks.Options]; - - if (dev) { - elems.push(safeGetElement("dev-tab")); - links.push(safeGetElement("dev-menu-link")); - } - - if (tutorial.style.maxHeight) { - toggleHeader(false, elems, links); - } else { - toggleHeader(true, elems, links); - } - }; - - return true; - } catch (e) { - console.error(`Failed to initialize Main Menu Headers: ${e}`); - return false; - } -} diff --git a/src/ui/MainMenu/Links.ts b/src/ui/MainMenu/Links.ts deleted file mode 100644 index b89ce5757..000000000 --- a/src/ui/MainMenu/Links.ts +++ /dev/null @@ -1,98 +0,0 @@ -// Get references to the Main Menu link DOM elements -// Does NOT include collapsible headers for the links -import { clearEventListeners } from "../../../utils/uiHelpers/clearEventListeners"; - -interface IMainMenuLinks { - [key: string]: HTMLElement | undefined; - Terminal: HTMLElement; - ScriptEditor: HTMLElement; - ActiveScripts: HTMLElement; - CreateProgram: HTMLElement; - Stats: HTMLElement; - Factions: HTMLElement; - Augmentations: HTMLElement; - HacknetNodes: HTMLElement; - Sleeves: HTMLElement; - City: HTMLElement; - Travel: HTMLElement; - Job: HTMLElement; - StockMarket: HTMLElement; - Bladeburner: HTMLElement; - Corporation: HTMLElement; - Gang: HTMLElement; - Milestones: HTMLElement; - Tutorial: HTMLElement; - Options: HTMLElement; - DevMenu: HTMLElement; -} - -const emptyElement: HTMLElement = ((): HTMLElement => { - const elem = document.createElement("div"); - if (elem === null) throw new Error("unable to create empty div element"); - return elem; -})(); - -export const MainMenuLinks: IMainMenuLinks = { - Terminal: emptyElement, - ScriptEditor: emptyElement, - ActiveScripts: emptyElement, - CreateProgram: emptyElement, - Stats: emptyElement, - Factions: emptyElement, - Augmentations: emptyElement, - HacknetNodes: emptyElement, - Sleeves: emptyElement, - City: emptyElement, - Travel: emptyElement, - Job: emptyElement, - StockMarket: emptyElement, - Bladeburner: emptyElement, - Corporation: emptyElement, - Gang: emptyElement, - Milestones: emptyElement, - Tutorial: emptyElement, - Options: emptyElement, - DevMenu: emptyElement, -}; - -export function initializeMainMenuLinks(): boolean { - return true; - try { - function safeGetLink(id: string): HTMLElement { - const elem: HTMLElement | null = clearEventListeners(id); - if (elem == null) { - throw new Error(`clearEventListeners() failed for element with id: ${id}`); - } - - return elem; - } - - MainMenuLinks.Terminal = safeGetLink("terminal-menu-link"); - MainMenuLinks.ScriptEditor = safeGetLink("create-script-menu-link"); - MainMenuLinks.ActiveScripts = safeGetLink("active-scripts-menu-link"); - MainMenuLinks.CreateProgram = safeGetLink("create-program-menu-link"); - MainMenuLinks.Stats = safeGetLink("stats-menu-link"); - MainMenuLinks.Factions = safeGetLink("factions-menu-link"); - MainMenuLinks.Augmentations = safeGetLink("augmentations-menu-link"); - MainMenuLinks.HacknetNodes = safeGetLink("hacknet-nodes-menu-link"); - MainMenuLinks.Sleeves = safeGetLink("sleeves-menu-link"); - MainMenuLinks.City = safeGetLink("city-menu-link"); - MainMenuLinks.Travel = safeGetLink("travel-menu-link"); - MainMenuLinks.Job = safeGetLink("job-menu-link"); - MainMenuLinks.StockMarket = safeGetLink("stock-market-menu-link"); - MainMenuLinks.Bladeburner = safeGetLink("bladeburner-menu-link"); - MainMenuLinks.Corporation = safeGetLink("corporation-menu-link"); - MainMenuLinks.Gang = safeGetLink("gang-menu-link"); - MainMenuLinks.Milestones = safeGetLink("milestones-menu-link"); - MainMenuLinks.Tutorial = safeGetLink("tutorial-menu-link"); - // const op: HTMLElement | null = document.getElementById("options-menu-link"); - // if (op === null) throw new Error(`Could not find element with id: "options-menu-link"`); - // MainMenuLinks.Options = op; // This click listener is already set, so don't clear it - MainMenuLinks.DevMenu = safeGetLink("dev-menu-link"); - - return true; - } catch (e) { - console.error(`Failed to initialize Main Menu Links: ${e}`); - return false; - } -} diff --git a/src/ui/React/GameOptionsRoot.tsx b/src/ui/React/GameOptionsRoot.tsx index b557e22d4..e62d4e45b 100644 --- a/src/ui/React/GameOptionsRoot.tsx +++ b/src/ui/React/GameOptionsRoot.tsx @@ -195,7 +195,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement { return; } const contents = result; - save(contents).then(() => location.reload()); + save(contents).then(() => setTimeout(() => location.reload(), 1000)); }; reader.readAsText(file); } @@ -628,7 +628,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement { onConfirm={() => { setDeleteOpen(false); deleteGame() - .then(() => location.reload()) + .then(() => setTimeout(() => location.reload(), 1000)) .catch((r) => console.error(`Could not delete game: ${r}`)); }} open={deleteGameOpen}