diff --git a/src/Enums.ts b/src/Enums.ts index d38f0727c..de46e6828 100644 --- a/src/Enums.ts +++ b/src/Enums.ts @@ -9,6 +9,7 @@ export * from "./Faction/Enums"; export * from "./Literature/Enums"; export * from "./Locations/Enums"; export * from "./Message/Enums"; +export * from "./Myrian/Enums"; export * from "./Programs/Enums"; export * from "./StockMarket/Enums"; export * from "./ui/Enums"; diff --git a/src/Myrian/Enums.ts b/src/Myrian/Enums.ts new file mode 100644 index 000000000..bcb55b263 --- /dev/null +++ b/src/Myrian/Enums.ts @@ -0,0 +1,3 @@ +export enum MyrianActions { + MOVE = "MOVE", +} diff --git a/src/Myrian/Myrian.ts b/src/Myrian/Myrian.ts index 5d793cd75..063882ecf 100644 --- a/src/Myrian/Myrian.ts +++ b/src/Myrian/Myrian.ts @@ -1,7 +1,7 @@ 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"; +import { DefaultWorld } from "./World"; interface MyrianSleeve { index: number; @@ -38,7 +38,8 @@ export class Myrian { /** Initializes a Myrian object from a JSON save state. */ static fromJSON(value: IReviverValue): Myrian { - return Generic_fromJSON(Myrian, value.data); + const v = Generic_fromJSON(Myrian, value.data); + return v; } } diff --git a/src/Myrian/World.ts b/src/Myrian/World.ts index 5d8ff114e..4e7de875d 100644 --- a/src/Myrian/World.ts +++ b/src/Myrian/World.ts @@ -1,12 +1,38 @@ -const e = ''; // empty -const c = 'c'; // core -const b = 'b'; // battery -const d = 'd'; // depleted +const e = " "; // empty +const c = "c"; // core +const b = "b"; // battery +const d = "d"; // depleted -export const DefaultWorld = [ - [b, b, b, b, b], - [e, e, e, e, e], - [e, e, c, e, e], - [e, e, e, e, e], - [b, b, b, d, d], -]; \ No newline at end of file +const raw = ` + + mmmmmmm + mmmmmmmm + mmmmmmmmmmmm + bbbb mmmmm + mmmm + c mmmm + mmmm + ddddd mmmm + + mmmmmmmmmm + mmmmmmmm + mmmmmmmm mmmm + + + + + + + + + + + + + + + + `; + +export const DefaultWorld = raw.split("\n").map((l) => l.split("")); +console.log(DefaultWorld); diff --git a/src/Myrian/ui/MyrianRoot.tsx b/src/Myrian/ui/MyrianRoot.tsx index a58420682..873899a4c 100644 --- a/src/Myrian/ui/MyrianRoot.tsx +++ b/src/Myrian/ui/MyrianRoot.tsx @@ -7,6 +7,7 @@ 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"; +import LandscapeIcon from "@mui/icons-material/Landscape"; const iterator = (i: number): number[] => { return Array(i).fill(0); @@ -26,17 +27,20 @@ const Cell = ({ tile }: ICellProps): React.ReactElement => { return (
- {tile === "" &&
} + {tile === " " &&
} {tile === "b" && } {tile === "d" && } {tile === "c" && } {tile === "s" && } + {tile === "m" && }
); }; @@ -49,7 +53,7 @@ export function MyrianRoot({ myrian }: IProps): React.ReactElement { const [, setRerender] = useState(false); const rerender = () => setRerender((old) => !old); useEffect(() => { - const intervalID = setInterval(rerender, 200); + const intervalID = setInterval(rerender, 20); return () => clearInterval(intervalID); }, []); diff --git a/src/Myrian/world.txt b/src/Myrian/world.txt new file mode 100644 index 000000000..4baa940ba --- /dev/null +++ b/src/Myrian/world.txt @@ -0,0 +1,30 @@ + + + mmmmmmm + mmmmmmmm + mmmmmmmmmmmm + bbbb mmmmm + mmmm + c mmmm + mmmm + ddddd mmmm + + mmmmmmmmmm + mmmmmmmm + mmmmmmmm mmmm + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index f021c7ca3..5b2e761ab 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -1,9 +1,23 @@ import { Player } from "@player"; -import { NSFull } from "../NetscriptFunctions"; +import { NS } from "@nsdefs"; +import { INetscriptExtra } from "../NetscriptFunctions/Extra"; + +// Made with the help of this question +// https://stackoverflow.com/questions/76723668/how-to-make-a-typescript-utility-type-representing-only-the-functions-of-an-obje?noredirect=1#comment135264239_76723668 +type EmptyObjToNever = {} extends T ? never : T; + +type PickFuncs = T extends Function + ? T + : T extends readonly any[] + ? never + : T extends object + ? EmptyObjToNever<{ [K in keyof T as PickFuncs extends never ? never : K]: PickFuncs }> + : never; -/** The API does not include enums, args, or pid. */ export type RamCostTree = { - [key in keyof API]: API[key] extends () => unknown ? number | (() => number) : RamCostTree; + [key in keyof PickFuncs]: PickFuncs[key] extends () => unknown + ? number | (() => number) + : RamCostTree[key]>; }; /** Constants for assigning costs to ns functions */ @@ -326,15 +340,16 @@ const stanek = { } as const; const myr = { - ianUse: 5.9, - ianMove: 3.4, + ianAct: 5.9, + ianGetTile: 1.4, + ianWorldSize: 3.333, + ianGetSleeve: 2, ianGetTask: 1.1, ianCancelTask: 1.2, ianEnter: 0.2, ianLeave: 0.2, - ianDeploy: 4.1, ianApplyPowerup: 10.9, -}; +} as const; // UI API const ui = { @@ -424,7 +439,7 @@ const corporation = { * An error will be generated if there are missing OR additional ram costs defined. * To avoid errors, define every function in NetscriptDefinition.d.ts and NetscriptFunctions, * and have a ram cost associated here. */ -export const RamCosts: RamCostTree = { +export const RamCosts: RamCostTree = { corporation, hacknet, stock, diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 446cb3e5c..a45e4d531 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -24,6 +24,7 @@ import { LocationName, ToastVariant, UniversityClassType, + MyrianActions, } from "@enums"; import { PromptEvent } from "./ui/React/PromptManager"; import { GetServer, DeleteServer, AddToAllServers, createUniqueRandomIp } from "./Server/AllServers"; @@ -112,6 +113,7 @@ export const enums: NSEnums = { LocationName, ToastVariant, UniversityClassType, + MyrianActions, }; for (const val of Object.values(enums)) Object.freeze(val); Object.freeze(enums); diff --git a/src/NetscriptFunctions/Myrian.ts b/src/NetscriptFunctions/Myrian.ts index 8334fa286..3f0cde52a 100644 --- a/src/NetscriptFunctions/Myrian.ts +++ b/src/NetscriptFunctions/Myrian.ts @@ -1,28 +1,56 @@ import { Myr as IMyrian } from "@nsdefs"; -import { InternalAPI } from "src/Netscript/APIWrapper"; +import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { helpers } from "../Netscript/NetscriptHelpers"; import { Player as player } from "../Player"; import { myrian } from "../Myrian/Helpers"; +import { MyrianActions } from "@enums"; + +const move = (ctx: NetscriptContext, id: number, x: number, y: number) => { + if (!player.sleeves[id]) throw new Error(`No sleeve with index ${id}`); + const myrSleeve = myrian.sleeves.find((s) => s.index === id); + if (!myrSleeve) throw new Error("Invalid move"); + const dist = Math.abs(myrSleeve.x - x) + Math.abs(myrSleeve.y - y); + if (dist > 1) throw new Error("Invalid move"); + return helpers.netscriptDelay(ctx, 100).then(function () { + myrSleeve.x = x; + myrSleeve.y = y; + return Promise.resolve(); + }); +}; export function NetscriptMyrian(): InternalAPI { return { - ianUse: (ctx) => (_sleeveId, _x, _y) => { - throw new Error("Unimplemented"); - }, - ianMove: (ctx) => async (_sleeveId, _x, _y) => { - const id = helpers.number(ctx, "sleeveId", _sleeveId); + ianAct: (ctx) => (_action, _sleeveId, _x, _y) => { + const action = helpers.string(ctx, "action", _action); 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(); - }); + const sleeveId = helpers.number(ctx, "sleeveId", _sleeveId); + switch (action) { + case MyrianActions.MOVE: + return move(ctx, sleeveId, x, y); + } + return Promise.reject("Invalid action"); + }, + + ianWorldSize: (ctx) => () => { + return [myrian.world.length, myrian.world[0].length]; + }, + + ianGetSleeve: (ctx) => (_sleeveId) => { + const sleeveId = helpers.number(ctx, "sleeveId", _sleeveId); + const sl = myrian.sleeves.find((s) => s.index === sleeveId); + if (!sl) { + return { inside: false, x: 0, y: 0 }; + } + return { inside: true, x: sl.x, y: sl.y }; + }, + + ianGetTile: (ctx) => (_x, _y) => { + const x = helpers.number(ctx, "x", _x); + const y = helpers.number(ctx, "y", _y); + return { + Content: myrian.world[y][x], + }; }, ianGetTask: (ctx) => (_sleeveId) => { throw new Error("Unimplemented"); @@ -41,9 +69,6 @@ export function NetscriptMyrian(): InternalAPI { 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/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 85044aa56..71977669c 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -1,5 +1,7 @@ /** All netscript definitions */ +import { string } from "prop-types"; + /** @public */ interface HP { current: number; @@ -2710,6 +2712,20 @@ export interface Hacknet { getTrainingMult(): number; } +export interface MyrianTile { + Content: string; +} + +export interface MyrianSleeve { + x: number; + y: number; + inside: boolean; +} + +export declare enum MyrianActions { + MOVE = "MOVE", +} + /** * Myrian API * @remarks @@ -2719,28 +2735,18 @@ export interface Hacknet { */ export interface Myr { /** - * Interact with an object in The Myrian. - * @remarks + * Take an action in The Myrian. * - * 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. + * @returns A promise to be resolved when the action is over. */ - ianUse(sleeveId: number, x: number, y: number): number; + ianAct(action: MyrianActions, sleeveId: number, x: number, y: number): Promise; /** - * Move a sleeve in the Myrian. - * @remarks + * Get the content of a tile. * - * The target tile must be 1 tile away from the sleeves current tile. - * - * @returns Amount of milliseconds the operation will take. + * @returns The content of the tile. */ - ianMove(sleeveId: number, x: number, y: number): Promise; + ianGetTile(x: number, y: number): MyrianTile; /** * Get that sleeves current task in the Myrian. @@ -2759,6 +2765,18 @@ export interface Myr { */ ianCancelTask(sleeveId): boolean; + /** + * Get the size of the world. + * @returns [width, height] of the world. + */ + ianWorldSize(): [number, number]; + + /** + * Get information about a sleeve and it's property related to The Myrian. + * @returns Information about a sleeve in The Myrian. + */ + ianGetSleeve(sleeveId: number): MyrianSleeve; + /** * Makes the player or a sleeve enter The Myrian. * @remarks @@ -2777,16 +2795,6 @@ export interface Myr { */ ianLeave(sleeveId?: number): boolean; - /** - * 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 @@ -6964,6 +6972,7 @@ export type NSEnums = { LocationName: typeof LocationName; ToastVariant: typeof ToastVariant; UniversityClassType: typeof UniversityClassType; + MyrianActions: typeof MyrianActions; }; /** diff --git a/tsconfig.json b/tsconfig.json index 28127e2fc..9db716a98 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "target": "es2022" + "target": "es2022" }, "include": ["src/**/*", "electron/**/*", "node_modules/monaco-editor/monaco.d.ts"] }