From 6924d27ba7c47f26e7ffd2aa7cf8fa188d57af73 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 17 Jul 2023 17:33:22 -0400 Subject: [PATCH] More work on bn14 --- src/Myrian/Helpers.ts | 11 +- src/Myrian/Myrian.ts | 48 +++++-- src/Myrian/ui/MyrianRoot.tsx | 98 +++++++------- src/Netscript/RamCostGenerator.ts | 6 +- src/NetscriptFunctions/Myrian.ts | 80 ++++++----- .../Sleeve/Work/SleeveMyrianWork.ts | 35 +++-- src/PersonObjects/Sleeve/ui/SleeveElem.tsx | 4 +- src/PersonObjects/Sleeve/ui/TaskSelector.tsx | 6 +- src/SaveObject.ts | 15 ++- src/ScriptEditor/NetscriptDefinitions.d.ts | 124 ++++++++---------- src/Sidebar/ui/SidebarRoot.tsx | 2 + src/ui/GameRoot.tsx | 2 +- 12 files changed, 244 insertions(+), 187 deletions(-) diff --git a/src/Myrian/Helpers.ts b/src/Myrian/Helpers.ts index 4245d7f02..87d4a1cdb 100644 --- a/src/Myrian/Helpers.ts +++ b/src/Myrian/Helpers.ts @@ -1,3 +1,12 @@ +import { Reviver } from "../utils/JSONReviver"; import { Myrian } from "./Myrian"; -export const myrian = new Myrian(); \ No newline at end of file +export let myrian = new Myrian(); + +export function loadMyrian(saveString: string): void { + if (saveString) { + myrian = JSON.parse(saveString, Reviver); + } else { + myrian = new Myrian(); + } +} diff --git a/src/Myrian/Myrian.ts b/src/Myrian/Myrian.ts index d0979e2f5..5d793cd75 100644 --- a/src/Myrian/Myrian.ts +++ b/src/Myrian/Myrian.ts @@ -1,13 +1,45 @@ +import { SleeveMyrianWork } from "../PersonObjects/Sleeve/Work/SleeveMyrianWork"; import { DefaultWorld } from "./World"; +import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver"; +import { Player } from "@player"; +interface MyrianSleeve { + index: number; + x: number; + y: number; +} export class Myrian { - world: string[][]; - resources: number; + world: string[][] = []; + resources = 0; + sleeves: MyrianSleeve[] = []; - constructor() { - this.world = DefaultWorld; - this.resources = 0; - console.log(this); - } -} \ No newline at end of file + constructor() { + this.world = DefaultWorld; + this.resources = 0; + } + + joinSleeve(sleeveId: number) { + if (this.sleeves.find((m) => m.index === sleeveId)) return; + const spawn = this.findSleeveSpawnPoint(); + Player.sleeves[sleeveId].startWork(new SleeveMyrianWork()); + this.sleeves.push({ index: sleeveId, x: spawn[0], y: spawn[0] }); + } + + findSleeveSpawnPoint(): [number, number] { + // Wrong but will do for now + return [1, 1]; + } + + /** Serialize the current object to a JSON save state. */ + toJSON(): IReviverValue { + return Generic_toJSON("Myrian", this); + } + + /** Initializes a Myrian object from a JSON save state. */ + static fromJSON(value: IReviverValue): Myrian { + return Generic_fromJSON(Myrian, value.data); + } +} + +constructorsForReviver.Myrian = Myrian; diff --git a/src/Myrian/ui/MyrianRoot.tsx b/src/Myrian/ui/MyrianRoot.tsx index ac23bbd09..a58420682 100644 --- a/src/Myrian/ui/MyrianRoot.tsx +++ b/src/Myrian/ui/MyrianRoot.tsx @@ -1,67 +1,71 @@ import React, { useEffect, useState } from "react"; import Container from "@mui/material/Container"; import { Box } from "@mui/system"; -import { Paper, Typography } from "@mui/material"; -import { useRerender } from "src/ui/React/hooks"; import { Myrian } from "../Myrian"; -import PersonIcon from '@mui/icons-material/Person'; -import BatteryFullIcon from '@mui/icons-material/BatteryFull'; -import Battery20Icon from '@mui/icons-material/Battery20'; -import FavoriteIcon from '@mui/icons-material/Favorite'; - - -const width = 30; -const height = 30; +import PersonIcon from "@mui/icons-material/Person"; +import BatteryFullIcon from "@mui/icons-material/BatteryFull"; +import Battery20Icon from "@mui/icons-material/Battery20"; +import FavoriteIcon from "@mui/icons-material/Favorite"; const iterator = (i: number): number[] => { - return Array(i).fill(0) -} + return Array(i).fill(0); +}; interface ICellProps { - tile: string; + tile: string; } const Cell = ({ tile }: ICellProps): React.ReactElement => { - const x = 50; - const sx = { - display: 'block', - color: 'white', - fontSize: x + 'px', - } - return
- {tile === '' &&
} - {tile === 'b' && } - {tile === 'd' && } - {tile === 'c' && } + const x = 50; + const sx = { + display: "block", + color: "white", + fontSize: x + "px", + }; + return ( +
+ {tile === "" &&
} + {tile === "b" && } + {tile === "d" && } + {tile === "c" && } + {tile === "s" && }
-} + ); +}; interface IProps { - myrian: Myrian; + myrian: Myrian; } export function MyrianRoot({ myrian }: IProps): React.ReactElement { - const [render, setRerender] = useState(false); - const rerender = () => setRerender((old) => !old); - useEffect(() => { - const intervalID = setInterval(rerender, 200); - return () => clearInterval(intervalID); - }, []); + const [, setRerender] = useState(false); + const rerender = () => setRerender((old) => !old); + useEffect(() => { + const intervalID = setInterval(rerender, 200); + return () => clearInterval(intervalID); + }, []); - return ( - - - {iterator(myrian.world.length).map((_, j) => - - {iterator(myrian.world[j].length).map((_, i) => - ) - } - ) - } - - - ); + const sleeves = Object.fromEntries(myrian.sleeves.map((s) => [`${s.x}_${s.y}`, s])); + + return ( + + + {iterator(myrian.world.length).map((_, j) => ( + + {iterator(myrian.world[j].length).map((_, i) => ( + + ))} + + ))} + + + ); } diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index 6af9751eb..f021c7ca3 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -326,15 +326,15 @@ const stanek = { } as const; const myr = { - ianInteract: 5.9, + ianUse: 5.9, ianMove: 3.4, ianGetTask: 1.1, ianCancelTask: 1.2, ianEnter: 0.2, ianLeave: 0.2, - ianBuild: 4.1, + ianDeploy: 4.1, ianApplyPowerup: 10.9, -} +}; // UI API const ui = { diff --git a/src/NetscriptFunctions/Myrian.ts b/src/NetscriptFunctions/Myrian.ts index 465efdeb2..8334fa286 100644 --- a/src/NetscriptFunctions/Myrian.ts +++ b/src/NetscriptFunctions/Myrian.ts @@ -1,41 +1,51 @@ - import { Myr as IMyrian } from "@nsdefs"; import { InternalAPI } from "src/Netscript/APIWrapper"; import { helpers } from "../Netscript/NetscriptHelpers"; import { Player as player } from "../Player"; -import { SleeveMyrianWork } from "../PersonObjects/Sleeve/Work/SleeveMyrianWork"; +import { myrian } from "../Myrian/Helpers"; export function NetscriptMyrian(): InternalAPI { - return { - ianInteract: - (ctx) => - (sleeveId, x, y) => { throw new Error("Unimplemented"); }, - ianMove: - (ctx) => - (sleeveId, x, y) => { throw new Error("Unimplemented"); }, - ianGetTask: - (ctx) => - (sleeveId) => { throw new Error("Unimplemented"); }, - ianCancelTask: - (ctx) => - (sleeveId) => { throw new Error("Unimplemented"); }, - ianEnter: - (ctx) => - (sleeveId?) => { - const id = sleeveId === undefined ? undefined : helpers.number(ctx, "sleeveId", sleeveId); - if (id === undefined) return false; // skip player handling for now. - // handle sleeve entering the myrian. - player.sleeves[id].startWork(new SleeveMyrianWork()); - return true; - }, - ianLeave: - (ctx) => - (sleeveId?) => { throw new Error("Unimplemented"); }, - ianBuild: - (ctx) => - (sleeveId, buildingId, x, y) => { throw new Error("Unimplemented"); }, - ianApplyPowerup: - (ctx) => - (sleeveId, stat) => { throw new Error("Unimplemented"); }, - } -} \ No newline at end of file + return { + ianUse: (ctx) => (_sleeveId, _x, _y) => { + throw new Error("Unimplemented"); + }, + ianMove: (ctx) => async (_sleeveId, _x, _y) => { + const id = helpers.number(ctx, "sleeveId", _sleeveId); + const x = helpers.number(ctx, "x", _x); + const y = helpers.number(ctx, "y", _y); + if (!player.sleeves[id]) throw new Error(`No sleeve with index ${id}`); + const myrSleeve = myrian.sleeves.find((s) => s.index === id); + if (!myrSleeve) return Promise.resolve(); + const dist = Math.abs(myrSleeve.x - x) + Math.abs(myrSleeve.y - y); + if (dist > 1) return Promise.resolve(); + return helpers.netscriptDelay(ctx, 1000).then(function () { + myrSleeve.x = x; + myrSleeve.y = y; + return Promise.resolve(); + }); + }, + ianGetTask: (ctx) => (_sleeveId) => { + throw new Error("Unimplemented"); + }, + ianCancelTask: (ctx) => (_sleeveId) => { + throw new Error("Unimplemented"); + }, + ianEnter: (ctx) => (sleeveId?) => { + const id = sleeveId === undefined ? undefined : helpers.number(ctx, "sleeveId", sleeveId); + if (id === undefined) return false; // skip player handling for now. + // handle sleeve entering the myrian. + if (!player.sleeves[id]) throw new Error(`No sleeve with index ${id}`); + myrian.joinSleeve(id); + return true; + }, + ianLeave: (ctx) => (_sleeveId?) => { + throw new Error("Unimplemented"); + }, + ianDeploy: (ctx) => (_sleeveId, _deploymentId, _x, _y) => { + throw new Error("Unimplemented"); + }, + ianApplyPowerup: (ctx) => (_sleeveId, _stat) => { + throw new Error("Unimplemented"); + }, + }; +} diff --git a/src/PersonObjects/Sleeve/Work/SleeveMyrianWork.ts b/src/PersonObjects/Sleeve/Work/SleeveMyrianWork.ts index c061ceebc..48e35c29a 100644 --- a/src/PersonObjects/Sleeve/Work/SleeveMyrianWork.ts +++ b/src/PersonObjects/Sleeve/Work/SleeveMyrianWork.ts @@ -1,29 +1,28 @@ import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver"; import { Sleeve } from "../Sleeve"; -import { Work, WorkType } from "./Work"; -import { calculateIntelligenceBonus } from "../../formulas/intelligence"; +import { SleeveWork, SleeveWorkClass, SleeveWorkType } from "./Work"; -export const isSleeveMyrianWork = (w: Work | null): w is SleeveMyrianWork => - w !== null && w.type === WorkType.MYRIAN; +export const isSleeveMyrianWork = (w: SleeveWork | null): w is SleeveMyrianWork => + w !== null && w.type === SleeveWorkType.MYRIAN; -export class SleeveMyrianWork extends Work { - type: WorkType.MYRIAN = WorkType.MYRIAN; +export class SleeveMyrianWork extends SleeveWorkClass { + type: SleeveWorkType.MYRIAN = SleeveWorkType.MYRIAN; - process(sleeve: Sleeve, cycles: number) { } + process(sleeve: Sleeve, cycles: number) {} - APICopy() { - return { type: WorkType.MYRIAN as "MYRIAN" }; - } + APICopy() { + return { type: SleeveWorkType.MYRIAN as "MYRIAN" }; + } - /** Serialize the current object to a JSON save state. */ - toJSON(): IReviverValue { - return Generic_toJSON("SleeveRecoveryWork", this); - } + /** Serialize the current object to a JSON save state. */ + toJSON(): IReviverValue { + return Generic_toJSON("SleeveRecoveryWork", this); + } - /** Initializes a RecoveryWork object from a JSON save state. */ - static fromJSON(value: IReviverValue): SleeveMyrianWork { - return Generic_fromJSON(SleeveMyrianWork, value.data); - } + /** Initializes a RecoveryWork object from a JSON save state. */ + static fromJSON(value: IReviverValue): SleeveMyrianWork { + return Generic_fromJSON(SleeveMyrianWork, value.data); + } } constructorsForReviver.SleeveMyrianWork = SleeveMyrianWork; diff --git a/src/PersonObjects/Sleeve/ui/SleeveElem.tsx b/src/PersonObjects/Sleeve/ui/SleeveElem.tsx index a8a3c0787..918fd6e18 100644 --- a/src/PersonObjects/Sleeve/ui/SleeveElem.tsx +++ b/src/PersonObjects/Sleeve/ui/SleeveElem.tsx @@ -57,8 +57,8 @@ function getWorkDescription(sleeve: Sleeve, progress: number): string { "This sleeve is currently attempting to infiltrate synthoid communities to generate additional contracts and operations.\nThis activity is less efficient the more sleeves are assigned to it.\n\n" + `Progress: ${formatPercent(progress)}` ); - case WorkType.MYRIAN: - return "This sleeve is currently in The Myrian." + case SleeveWorkType.MYRIAN: + return "This sleeve is currently in The Myrian."; } } diff --git a/src/PersonObjects/Sleeve/ui/TaskSelector.tsx b/src/PersonObjects/Sleeve/ui/TaskSelector.tsx index 67c61ec86..14054525f 100644 --- a/src/PersonObjects/Sleeve/ui/TaskSelector.tsx +++ b/src/PersonObjects/Sleeve/ui/TaskSelector.tsx @@ -217,7 +217,7 @@ const tasks: { }, ["In The Myrian"]: (): ITaskDetails => { return { first: ["------"], second: () => ["------"] }; - } + }, }; const canDo: { @@ -243,7 +243,7 @@ const canDo: { "Perform Bladeburner Actions": () => !!Player.bladeburner, "Shock Recovery": (sleeve: Sleeve) => sleeve.shock > 0, Synchronize: (sleeve: Sleeve) => sleeve.sync < 100, - ["In The Myrian"]: (sleeve: Sleeve) => true, + ["In The Myrian"]: () => true, }; function getABC(sleeve: Sleeve): [string, string, string] { @@ -285,7 +285,7 @@ function getABC(sleeve: Sleeve): [string, string, string] { return ["Shock Recovery", "------", "------"]; case SleeveWorkType.SYNCHRO: return ["Synchronize", "------", "------"]; - case WorkType.MYRIAN: + case SleeveWorkType.MYRIAN: return ["In The Myrian", "------", "------"]; } } diff --git a/src/SaveObject.ts b/src/SaveObject.ts index a83317238..a3a5458c4 100644 --- a/src/SaveObject.ts +++ b/src/SaveObject.ts @@ -38,6 +38,7 @@ import { Corporation } from "./Corporation/Corporation"; import { Terminal } from "./Terminal"; import { getRecordValues } from "./Types/Record"; import { ExportMaterial } from "./Corporation/Actions"; +import { loadMyrian, myrian } from "./Myrian/Helpers"; /* SaveObject.js * Defines the object used to save/load games @@ -85,6 +86,7 @@ class BitburnerSaveObject { AllGangsSave = ""; LastExportBonus = "0"; StaneksGiftSave = ""; + myrianSave = ""; getSaveString(forceExcludeRunningScripts = false): string { this.PlayerSave = JSON.stringify(Player); @@ -104,6 +106,7 @@ class BitburnerSaveObject { this.VersionSave = JSON.stringify(CONSTANTS.VersionNumber); this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus); this.StaneksGiftSave = JSON.stringify(staneksGift); + this.myrianSave = JSON.stringify(myrian); if (Player.gang) this.AllGangsSave = JSON.stringify(AllGangs); @@ -716,6 +719,12 @@ function loadGame(saveString: string): boolean { loadCompanies(saveObj.CompaniesSave); loadFactions(saveObj.FactionsSave); + if (Object.hasOwn(saveObj, "myrianSave")) { + loadMyrian(saveObj.myrianSave); + } else { + console.warn(`Could not load Staneks Gift from save`); + loadMyrian(""); + } if (Object.hasOwn(saveObj, "StaneksGiftSave")) { loadStaneksGift(saveObj.StaneksGiftSave); } else { @@ -811,9 +820,9 @@ function createNewUpdateText() { () => dialogBoxCreate( "New update!\n" + - "Please report any bugs/issues through the GitHub repository " + - "or the Bitburner subreddit (reddit.com/r/bitburner).\n\n" + - CONSTANTS.LatestUpdate, + "Please report any bugs/issues through the GitHub repository " + + "or the Bitburner subreddit (reddit.com/r/bitburner).\n\n" + + CONSTANTS.LatestUpdate, ), 1000, ); diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index d54585189..85044aa56 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -2719,90 +2719,82 @@ export interface Hacknet { */ export interface Myr { /** - * Interact with an object in The Myrian. - * @remarks - f - * - * The effect is different depending on the object. - * Interacting with an enemy will attack it. - * With a resource node will mine it. - * With a power up will collect it. - * With a rock will try to break it. - * - * @returns Amount of milliseconds the operation will take. - */ - ianInteract(sleeveId: number, x: number, y: number): number; + * Interact with an object in The Myrian. + * @remarks + * + * The effect is different depending on the object. + * Interacting with an enemy will attack it. + * With a resource node will mine it. + * With a power up will collect it. + * With a rock will try to break it. + * + * @returns Amount of milliseconds the operation will take. + */ + ianUse(sleeveId: number, x: number, y: number): number; /** - * Move a sleeve in the Myrian. - * @remarks - f - * - * The target tile must be 1 tile away from the sleeves current tile. - * - * @returns Amount of milliseconds the operation will take. - */ - ianMove(sleeveId: number, x: number, y: number): number; + * Move a sleeve in the Myrian. + * @remarks + * + * The target tile must be 1 tile away from the sleeves current tile. + * + * @returns Amount of milliseconds the operation will take. + */ + ianMove(sleeveId: number, x: number, y: number): Promise; /** - * Get that sleeves current task in the Myrian. - * @remarks - f - * - * - * @returns The task currently being performed. - */ + * Get that sleeves current task in the Myrian. + * @remarks + * + * + * @returns The task currently being performed. + */ ianGetTask(sleeveId): any; /** - * Cancel a sleeves current Myrian task. - * @remarks - f - * - * @returns true if a task was cancelled. - */ + * Cancel a sleeves current Myrian task. + * @remarks + * + * @returns true if a task was cancelled. + */ ianCancelTask(sleeveId): boolean; /** - * Makes the player or a sleeve enter The Myrian. - * @remarks - f - * - * @returns true if the person is now in The Myrian. - */ + * Makes the player or a sleeve enter The Myrian. + * @remarks + * + * @returns true if the person is now in The Myrian. + */ ianEnter(sleeveId?: number): boolean; /** - * Makes the player or a sleeve leave The Myrian. - * @remarks - f - * - * Sleeves must be 1 tile away from the core. - * - * @returns true if the person is now in the simulated world. - */ + * Makes the player or a sleeve leave The Myrian. + * @remarks + * + * Sleeves must be 1 tile away from the core. + * + * @returns true if the person is now in the simulated world. + */ ianLeave(sleeveId?: number): boolean; /** - * Build an entity in The Myrian. - * @remarks - f - * - * Sleeves must be 1 tile away from the target tile and the player must have enough resources to build the entity. - * - * @returns The amount of milliseconds needed to complete the operation. or -1 if failed. - */ - ianBuild(sleeveId: number, buildingId: number, x: number, y: number): number; + * Deploy an entity in The Myrian. + * @remarks + * + * Sleeves must be 1 tile away from the target tile and the player must have enough resources to build the entity. + * + * @returns The amount of milliseconds needed to complete the operation. or -1 if failed. + */ + ianDeploy(sleeveId: number, deploymentId: number, x: number, y: number): number; /** - * Apply a Myrian powerup to a sleeve. - * @remarks - f - * - * Must have at least 1 powerup to apply. - * - * @returns True if the powerup was applied. - */ + * Apply a Myrian powerup to a sleeve. + * @remarks + * + * Must have at least 1 powerup to apply. + * + * @returns True if the powerup was applied. + */ ianApplyPowerup(sleeveId: number, stat: string): boolean; } diff --git a/src/Sidebar/ui/SidebarRoot.tsx b/src/Sidebar/ui/SidebarRoot.tsx index e08a7040d..d0f87781c 100644 --- a/src/Sidebar/ui/SidebarRoot.tsx +++ b/src/Sidebar/ui/SidebarRoot.tsx @@ -39,6 +39,7 @@ import EmojiEventsIcon from "@mui/icons-material/EmojiEvents"; // Achievements import AccountBoxIcon from "@mui/icons-material/AccountBox"; import PublicIcon from "@mui/icons-material/Public"; import LiveHelpIcon from "@mui/icons-material/LiveHelp"; +import BrokenImageIcon from "@mui/icons-material/BrokenImage"; import { Router } from "../../ui/GameRoot"; import { Page, isSimplePage } from "../../ui/Router"; @@ -350,6 +351,7 @@ export function SidebarRoot(props: { page: Page }): React.ReactElement { canBladeburner && { key_: Page.Bladeburner, icon: FormatBoldIcon }, canCorporation && { key_: Page.Corporation, icon: BusinessIcon }, canGang && { key_: Page.Gang, icon: SportsMmaIcon }, + { key_: Page.Myrian, icon: BrokenImageIcon }, ]} /> diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index 5a8f8583d..3e6df273f 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -113,7 +113,7 @@ export let Router: IRouter = { }, }; -function determineStartPage(): Page { +function determineStartPage() { return Page.Myrian; // WRONG if (RecoveryMode) return Page.Recovery; if (Player.currentWork !== null) return Page.Work;