mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 06:18:42 +02:00
Compare commits
1 Commits
d1b6acc57a
...
factory
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d8e1a6d24 |
@@ -192,9 +192,9 @@ async function pushSaveDataToSteamCloud(saveData, currentPlayerId) {
|
||||
*
|
||||
* Instead of implementing it, the old code (encoding in base64) is used here for backward compatibility.
|
||||
*/
|
||||
const content = Buffer.from(saveData).toString("base64");
|
||||
log.debug(`saveData: ${saveData.length} bytes`);
|
||||
log.debug(`Base64 string of saveData: ${content.length} bytes`);
|
||||
const content = saveData.toString("base64");
|
||||
log.debug(`Uncompressed: ${saveData.length} bytes`);
|
||||
log.debug(`Compressed: ${content.length} bytes`);
|
||||
log.debug(`Saving to Steam Cloud as ${steamSaveName}`);
|
||||
|
||||
try {
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from "react";
|
||||
import { Player } from "@player";
|
||||
import { CityName, FactionName } from "@enums";
|
||||
import { BitNodeMultipliers, replaceCurrentNodeMults } from "./BitNodeMultipliers";
|
||||
import { CorruptableText } from "../ui/React/CorruptableText";
|
||||
|
||||
class BitNode {
|
||||
// A short description, or tagline, about the BitNode
|
||||
@@ -480,6 +481,19 @@ export function initBitNodes() {
|
||||
</>
|
||||
),
|
||||
);
|
||||
|
||||
BitNodes.BitNode19 = new BitNode(
|
||||
19,
|
||||
2,
|
||||
"MyrianOS",
|
||||
"l̷i̵g̵h̴t̵ ̴a̷t̸ ̶t̵h̵e̸ ̶e̷n̵d̶ ̶o̸f̶ ̸t̴h̸e̴ ̸t̷u̶n̸n̸e̷l̵.̷",
|
||||
(
|
||||
<CorruptableText
|
||||
content={`yNjHLAgecI ASW1fQdKx5 n9DQ3rmHp3 mnv0XEdwH2 sBkAlBOPhx NohIDL9eRy TbIl8U3WKz 1wjnJ9iuwS VML36vYLNH K06StviNvI cRboTarefZ 7BSNntPpJj DfayVbfxU6 46xvOPQd2Y Ogyj2gnyLr FIND THE GLITCH IN ISHIMA S6E0Vpmxk6 GTF9dWvE6n EEGg7xvtYR Um8YIC0Qww PG4vauBKBk JWG8V1j5Z5 bfYYTTFnBY 7uoicoqIaV IeUu0F42aA EhTF7Fkxyt OBYgGSu0es bJQpenVoO6 L9cL39tRhh xfLroUMvY8 xmMckUHLSQ`}
|
||||
spoiler={false}
|
||||
/>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export const defaultMultipliers = new BitNodeMultipliers();
|
||||
@@ -987,6 +1001,64 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier
|
||||
WorldDaemonDifficulty: 5,
|
||||
});
|
||||
}
|
||||
case 19: {
|
||||
return new BitNodeMultipliers({
|
||||
MyrianPower: 10,
|
||||
|
||||
AgilityLevelMultiplier: 0.01,
|
||||
AugmentationMoneyCost: 100,
|
||||
AugmentationRepCost: 100,
|
||||
BladeburnerRank: 0.01,
|
||||
BladeburnerSkillCost: 100,
|
||||
CharismaLevelMultiplier: 0.01,
|
||||
ClassGymExpGain: 0.01,
|
||||
CodingContractMoney: 0.01,
|
||||
CompanyWorkExpGain: 0.01,
|
||||
CompanyWorkMoney: 0.01,
|
||||
CompanyWorkRepGain: 0.01,
|
||||
CorporationValuation: 0.01,
|
||||
CrimeExpGain: 0.01,
|
||||
CrimeMoney: 0.01,
|
||||
CrimeSuccessRate: 0.01,
|
||||
DaedalusAugsRequirement: 100,
|
||||
DefenseLevelMultiplier: 0.01,
|
||||
DexterityLevelMultiplier: 0.01,
|
||||
FactionPassiveRepGain: 0.01,
|
||||
FactionWorkExpGain: 0.01,
|
||||
FactionWorkRepGain: 0.01,
|
||||
FourSigmaMarketDataApiCost: 100,
|
||||
FourSigmaMarketDataCost: 100,
|
||||
GangSoftcap: 0.01,
|
||||
GangUniqueAugs: 0.01,
|
||||
GoPower: 0.01,
|
||||
HackExpGain: 0.01,
|
||||
HackingLevelMultiplier: 0.01,
|
||||
HackingSpeedMultiplier: 0.01,
|
||||
HacknetNodeMoney: 0.01,
|
||||
HomeComputerRamCost: 100,
|
||||
InfiltrationMoney: 0.01,
|
||||
InfiltrationRep: 0.01,
|
||||
ManualHackMoney: 0.01,
|
||||
PurchasedServerCost: 100,
|
||||
PurchasedServerSoftcap: 100,
|
||||
PurchasedServerLimit: 0.01,
|
||||
PurchasedServerMaxRam: 0.01,
|
||||
RepToDonateToFaction: 10000,
|
||||
ScriptHackMoney: 0.01,
|
||||
ScriptHackMoneyGain: 0.01,
|
||||
ServerGrowthRate: 0.01,
|
||||
ServerMaxMoney: 0.01,
|
||||
ServerStartingMoney: 0.01,
|
||||
ServerStartingSecurity: 100,
|
||||
ServerWeakenRate: 0.01,
|
||||
StrengthLevelMultiplier: 0.01,
|
||||
StaneksGiftPowerMultiplier: 0.01,
|
||||
StaneksGiftExtraSize: -100,
|
||||
WorldDaemonDifficulty: 100,
|
||||
CorporationSoftcap: 0.01,
|
||||
CorporationDivisions: 0.01,
|
||||
});
|
||||
}
|
||||
default: {
|
||||
throw new Error("Invalid BitNodeN");
|
||||
}
|
||||
|
||||
@@ -114,6 +114,9 @@ export class BitNodeMultipliers {
|
||||
*/
|
||||
ManualHackMoney = 1;
|
||||
|
||||
/** Influence how strongly Myrian improves bitnode multipliers */
|
||||
MyrianPower = 1;
|
||||
|
||||
/** Influence how much it costs to purchase a server */
|
||||
PurchasedServerCost = 1;
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | O O | O O | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | / __| \ | | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | O / | O | | O | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | <BitNodePortal n={19} level={n(19)} flume={props.flume} destroyedBitNode={destroyed} /> / | O | | O | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | |_/ |/ | \_ \_| | | | | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | | <BitNodePortal n={14} level={n(14)} flume={props.flume} destroyedBitNode={destroyed} /> | | O__/ | / \__ | | O | | | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | / /| O / \| | | | | | | </Typography>
|
||||
|
||||
@@ -156,48 +156,10 @@ export const CONSTANTS: {
|
||||
|
||||
// Also update doc/source/changelog.rst
|
||||
LatestUpdate: `
|
||||
## v2.6.2 dev - Last update 4 June 2024
|
||||
## v2.6.2 dev - Last update 22 May 2024
|
||||
|
||||
See 2.6.1 changelog at https://github.com/bitburner-official/bitburner-src/blob/v2.6.1/src/Documentation/doc/changelog.md
|
||||
|
||||
### CHANGES
|
||||
|
||||
- Hotfix (also backported to 2.6.1): Fixed an issue with invalid format on steam cloud save (@catloversg)
|
||||
- Augmentations: Adjusted handling of augmentations that affect starting money or programs (@jjclark1982)
|
||||
- Coding Contracts: Improved the performance of the All Valid Math Expressions contract checker (@yichizhng)
|
||||
- Coding Contracts: Simplified the Shortest Path contract checker (@gmcew)
|
||||
- Coding Contracts: Clarification on HammingCodes: Encoded Binary to Integer description (@gmcew)
|
||||
- Faction: Fixed some edge cases around Favor overflow (@catloversg)
|
||||
- Faction Invites: Code refactoring, all available invites are sent at once (@catloversg)
|
||||
- Faction UI: show which skills are relevant for each type of Faction work (@gmcew)
|
||||
- Font: Embedded the JetBrains Mono font as "JetBrainsMono" (@catloversg)
|
||||
- Go: Support playing manually as white against your own scripts (@ficocelliguy)
|
||||
- Go: Save a full game history to prevent repeat moves (@ficocelliguy)
|
||||
- Infiltration: Updated Slash game text to be less confusing (@catloversg)
|
||||
- Netscript API docs: Fixed some invalid usage issues + general type improvements (@catloversg, @ficocelliguy)
|
||||
- Programs UI: Changed time elapsed display to time left (@TheAimMan)
|
||||
- Servers: Game servers can now start with more than 1 core (@TheAimMan)
|
||||
- Scripts: Relative imports should now work correctly (@Caldwell-74)
|
||||
- Script Editor: Improved detection of possible infinite loops (@G4mingJon4s)
|
||||
- Script Editor: should now remember cursor location when switching tabs or game pages (@catloversg)
|
||||
- Skill XP: Fix an issue where in some cases, too much experience was needed to raise a skill from 1 to 2 (@catloversg)
|
||||
- Terminal: Improved autocompletion code for mixed case strings (@yichizhng)
|
||||
- Codebase: Partial migration away from outdated mui/styles (@Caldwell-74)
|
||||
|
||||
### SPOILER CHANGES
|
||||
|
||||
- Bladeburner: Added a button to stop the current action (@Kelenius)
|
||||
- Bladeburner UI: Display Black Operations in the expected order (@catloversg)
|
||||
- Corporation: Allow mass discarding products by selling for 0 (@gmcew)
|
||||
- Grafting: Fixed a spacing issue (@Sphyxis)
|
||||
- Grafting/Hacknet: Fixed an issue that could cause hacknet node production to be inaccurrate when combined with Grafting (@catloversg)
|
||||
- Grafting: Fixed an issue that could cause inaccurate HP after Grafting (@catloversg)
|
||||
- Hashnet: Clarified effect of hacknet multipliers in in documentation (@catloversg)
|
||||
- Sleeve: Sleeve travel can no longer be performed if the player has insufficient funds (@gmcew)
|
||||
- Sleeve: Added a missing availability check when installing augmentations on Sleeves (@yichizhng)
|
||||
- Sleeve API: Fix an issue in ns.sleeve.setToBladeburnerAction that prevented setting sleeves to contract work (@Sphyxis)
|
||||
|
||||
### OTHER
|
||||
- Nerf noodle bar
|
||||
No changes yet since 2.6.1 release
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -14,3 +14,4 @@ export * from "./Programs/Enums";
|
||||
export * from "./StockMarket/Enums";
|
||||
export * from "./ui/Enums";
|
||||
export * from "./Work/Enums";
|
||||
export * from "./Myrian/Enums";
|
||||
|
||||
@@ -300,11 +300,36 @@ export function SpecialLocation(props: SpecialLocationProps): React.ReactElement
|
||||
}
|
||||
|
||||
function renderGlitch(): React.ReactElement {
|
||||
const onClick = () => {
|
||||
dialogBoxCreate(
|
||||
"Hexabytes of information are streamed to your mind. Completely drained one thing remained clear as crystal. You now understand how to connect directly to the machine running the bitnodes. Myrian.",
|
||||
);
|
||||
Router.toPage(Page.MyrianOS);
|
||||
Player.myrianConnection = true;
|
||||
};
|
||||
if (!Player.canAccessMyrian())
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
<CorruptableText
|
||||
content={"An eerie aura surrounds this area. You feel you should leave."}
|
||||
spoiler={false}
|
||||
/>
|
||||
</Typography>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
<CorruptableText content={"An eerie aura surrounds this area. You feel you should leave."} spoiler={false} />
|
||||
You find yourself standing in a small, unremarkable alley. Despite the lack of air you do not feel the need to
|
||||
breathe. There is no light, yet you can see every details of every objects in the alley. A rat walking in the
|
||||
alley completely stop in it's track as if frozen in time. You know what this means. This location has fallen
|
||||
through the crack of the Enders.
|
||||
<br />
|
||||
In the middle of the alley is a personal link port. You can connect your personal link to the anomaly.
|
||||
</Typography>
|
||||
<Button onClick={onClick}>Connect your personal link to the anomaly</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
100
src/Myrian/Enums.ts
Normal file
100
src/Myrian/Enums.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
export enum DeviceType {
|
||||
Bus = "bus",
|
||||
ISocket = "isocket",
|
||||
OSocket = "osocket",
|
||||
Reducer = "reducer",
|
||||
Cache = "cache",
|
||||
Lock = "lock",
|
||||
Battery = "battery",
|
||||
}
|
||||
|
||||
export enum Component {
|
||||
// tier 0
|
||||
R0 = "r0",
|
||||
G0 = "g0",
|
||||
B0 = "b0",
|
||||
|
||||
// tier 1
|
||||
R1 = "r1",
|
||||
G1 = "g1",
|
||||
B1 = "b1",
|
||||
|
||||
Y1 = "y1",
|
||||
C1 = "c1",
|
||||
M1 = "m1",
|
||||
|
||||
// tier 2
|
||||
R2 = "r2",
|
||||
G2 = "g2",
|
||||
B2 = "b2",
|
||||
|
||||
Y2 = "y2",
|
||||
C2 = "c2",
|
||||
M2 = "m2",
|
||||
|
||||
W2 = "w2",
|
||||
|
||||
// tier 3
|
||||
R3 = "r3",
|
||||
G3 = "g3",
|
||||
B3 = "b3",
|
||||
|
||||
Y3 = "y3",
|
||||
C3 = "c3",
|
||||
M3 = "m3",
|
||||
|
||||
W3 = "w3",
|
||||
|
||||
// tier 4
|
||||
R4 = "r4",
|
||||
G4 = "g4",
|
||||
B4 = "b4",
|
||||
|
||||
Y4 = "y4",
|
||||
C4 = "c4",
|
||||
M4 = "m4",
|
||||
|
||||
W4 = "w4",
|
||||
|
||||
// tier 5
|
||||
R5 = "r5",
|
||||
G5 = "g5",
|
||||
B5 = "b5",
|
||||
|
||||
Y5 = "y5",
|
||||
C5 = "c5",
|
||||
M5 = "m5",
|
||||
|
||||
W5 = "w5",
|
||||
|
||||
// tier 6
|
||||
Y6 = "y6",
|
||||
C6 = "c6",
|
||||
M6 = "m6",
|
||||
|
||||
W6 = "w6",
|
||||
|
||||
// tier 7
|
||||
W7 = "w7",
|
||||
}
|
||||
|
||||
export enum Glitch {
|
||||
// Locks spawn at random
|
||||
Segmentation = "segmentation",
|
||||
// ISockets and OSockets move around on their own
|
||||
Roaming = "roaming",
|
||||
// OSocket ask for more complicated components
|
||||
Encryption = "encryption",
|
||||
// Energy starts being consumed (level 0 is no consumption)
|
||||
Magnetism = "magnetism",
|
||||
// Hidden tiles on the board, when stepped on the bus loses upgrades
|
||||
Rust = "rust",
|
||||
// Move slows down
|
||||
Friction = "friction",
|
||||
// Transfer components and charging slows down
|
||||
Isolation = "isolation",
|
||||
// Install/Uninstall slows down
|
||||
Virtualization = "virtualization",
|
||||
// Reduce slows down
|
||||
Jamming = "jamming",
|
||||
}
|
||||
87
src/Myrian/Myrian.ts
Normal file
87
src/Myrian/Myrian.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { Device, DeviceID } from "@nsdefs";
|
||||
import { DeviceType, Component, Glitch } from "@enums";
|
||||
import { glitchMult } from "./formulas/glitches";
|
||||
import { isDeviceISocket, pickOne } from "./utils";
|
||||
import { componentTiers } from "./formulas/components";
|
||||
import { NewBus, NewISocket, NewOSocket } from "./NewDevices";
|
||||
import { startRoaming } from "./glitches/roaming";
|
||||
import { startRust } from "./glitches/rust";
|
||||
import { startSegmentation } from "./glitches/segmentation";
|
||||
import { startBattery } from "./glitches/battery";
|
||||
|
||||
export interface Myrian {
|
||||
vulns: number;
|
||||
totalVulns: number;
|
||||
devices: Device[];
|
||||
glitches: Record<Glitch, number>;
|
||||
rust: Record<string, boolean>;
|
||||
}
|
||||
|
||||
export const myrianSize = 12;
|
||||
|
||||
const defaultGlitches = Object.values(Glitch).reduce((acc, g) => ({ ...acc, [g]: 0 }), {}) as Record<Glitch, number>;
|
||||
|
||||
export const myrian: Myrian = {
|
||||
vulns: 0,
|
||||
totalVulns: 0,
|
||||
devices: [],
|
||||
glitches: { ...defaultGlitches },
|
||||
rust: {},
|
||||
};
|
||||
|
||||
export const loadMyrian = (save: string) => {
|
||||
resetMyrian();
|
||||
startRoaming();
|
||||
startRust();
|
||||
startSegmentation();
|
||||
startBattery();
|
||||
if (!save) return;
|
||||
const savedMyrian = JSON.parse(save);
|
||||
Object.assign(myrian, savedMyrian);
|
||||
myrian.devices.forEach((d) => (d.isBusy = false));
|
||||
myrian.devices.filter(isDeviceISocket).forEach((d) => (d.content = new Array(d.maxContent).fill(d.emitting)));
|
||||
};
|
||||
|
||||
export const inMyrianBounds = (x: number, y: number) => x >= 0 && x < myrianSize && y >= 0 && y < myrianSize;
|
||||
|
||||
export const findDevice = (id: DeviceID, type?: DeviceType): Device | undefined =>
|
||||
myrian.devices.find(
|
||||
(e) => (typeof id === "string" ? e.name === id : e.x === id[0] && e.y === id[1]) && (!type || type === e.type),
|
||||
);
|
||||
|
||||
export const removeDevice = (id: DeviceID, type?: DeviceType) => {
|
||||
myrian.devices = myrian.devices.filter(
|
||||
(e) => !((typeof id === "string" ? e.name === id : e.x === id[0] && e.y === id[1]) && (!type || type === e.type)),
|
||||
);
|
||||
};
|
||||
|
||||
export const getTotalGlitchMult = () =>
|
||||
Object.entries(myrian.glitches).reduce((acc, [glitch, lvl]) => {
|
||||
return acc * glitchMult(glitch as Glitch, lvl);
|
||||
}, 1);
|
||||
|
||||
export const getNextOSocketRequest = (tier: number) => {
|
||||
const potential = componentTiers.slice(0, tier + 1).flat();
|
||||
return new Array(Math.floor(Math.pow(Math.random() * tier, 0.75) + 1)).fill(null).map(() => pickOne(potential));
|
||||
};
|
||||
|
||||
export const countDevices = (type: DeviceType) =>
|
||||
myrian.devices.reduce((acc, d) => (d.type === type ? acc + 1 : acc), 0);
|
||||
|
||||
export const resetMyrian = () => {
|
||||
myrian.vulns = 0;
|
||||
myrian.totalVulns = 0;
|
||||
myrian.devices = [];
|
||||
myrian.glitches = { ...defaultGlitches };
|
||||
myrian.rust = {};
|
||||
|
||||
NewBus("alice", Math.floor(myrianSize / 2), Math.floor(myrianSize / 2));
|
||||
|
||||
NewISocket("isocket0", Math.floor(myrianSize / 4), 0, Component.R0);
|
||||
NewISocket("isocket1", Math.floor(myrianSize / 2), 0, Component.G0);
|
||||
NewISocket("isocket2", Math.floor((myrianSize * 3) / 4), 0, Component.B0);
|
||||
|
||||
NewOSocket("osocket0", Math.floor(myrianSize / 4), Math.floor(myrianSize - 1));
|
||||
NewOSocket("osocket1", Math.floor(myrianSize / 2), Math.floor(myrianSize - 1));
|
||||
NewOSocket("osocket2", Math.floor((myrianSize * 3) / 4), Math.floor(myrianSize - 1));
|
||||
};
|
||||
110
src/Myrian/NewDevices.ts
Normal file
110
src/Myrian/NewDevices.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { Battery, Bus, Cache, ISocket, Lock, OSocket, Reducer } from "@nsdefs";
|
||||
import { Component, DeviceType } from "@enums";
|
||||
import { myrian } from "./Myrian";
|
||||
import { getNextOSocketRequest } from "./Myrian";
|
||||
|
||||
export const NewBus = (name: string, x: number, y: number) => {
|
||||
const bus: Bus = {
|
||||
name,
|
||||
type: DeviceType.Bus,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
content: [],
|
||||
maxContent: 1,
|
||||
|
||||
moveLvl: 0,
|
||||
transferLvl: 0,
|
||||
reduceLvl: 0,
|
||||
installLvl: 0,
|
||||
energy: 16,
|
||||
maxEnergy: 16,
|
||||
};
|
||||
myrian.devices.push(bus);
|
||||
};
|
||||
|
||||
export const NewCache = (name: string, x: number, y: number) => {
|
||||
const cache: Cache = {
|
||||
name,
|
||||
type: DeviceType.Cache,
|
||||
isBusy: false,
|
||||
content: [],
|
||||
maxContent: 1,
|
||||
x,
|
||||
y,
|
||||
};
|
||||
myrian.devices.push(cache);
|
||||
return cache;
|
||||
};
|
||||
|
||||
export const NewReducer = (name: string, x: number, y: number) => {
|
||||
const reducer: Reducer = {
|
||||
name,
|
||||
type: DeviceType.Reducer,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
content: [],
|
||||
maxContent: 2,
|
||||
tier: 1,
|
||||
};
|
||||
myrian.devices.push(reducer);
|
||||
return reducer;
|
||||
};
|
||||
|
||||
export const NewISocket = (name: string, x: number, y: number, emitting: Component) => {
|
||||
const isocket: ISocket = {
|
||||
name,
|
||||
type: DeviceType.ISocket,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
emitting: emitting,
|
||||
emissionLvl: 0,
|
||||
cooldownUntil: 0,
|
||||
content: [emitting],
|
||||
maxContent: 1,
|
||||
};
|
||||
myrian.devices.push(isocket);
|
||||
};
|
||||
|
||||
export const NewOSocket = (name: string, x: number, y: number) => {
|
||||
const osocket: OSocket = {
|
||||
name,
|
||||
type: DeviceType.OSocket,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
currentRequest: getNextOSocketRequest(0),
|
||||
content: [],
|
||||
maxContent: 1,
|
||||
};
|
||||
myrian.devices.push(osocket);
|
||||
return osocket;
|
||||
};
|
||||
|
||||
export const NewLock = (name: string, x: number, y: number) => {
|
||||
const lock: Lock = {
|
||||
name,
|
||||
type: DeviceType.Lock,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
};
|
||||
myrian.devices.push(lock);
|
||||
return lock;
|
||||
};
|
||||
|
||||
export const NewBattery = (name: string, x: number, y: number) => {
|
||||
const battery: Battery = {
|
||||
name,
|
||||
type: DeviceType.Battery,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
tier: 0,
|
||||
energy: 64,
|
||||
maxEnergy: 64,
|
||||
};
|
||||
myrian.devices.push(battery);
|
||||
};
|
||||
12
src/Myrian/formulas/components.ts
Normal file
12
src/Myrian/formulas/components.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Component } from "@enums";
|
||||
|
||||
export const componentTiers = [
|
||||
[Component.R0, Component.G0, Component.B0],
|
||||
[Component.R1, Component.G1, Component.B1, Component.Y1, Component.C1, Component.M1],
|
||||
[Component.R2, Component.G2, Component.B2, Component.Y2, Component.C2, Component.M2, Component.W2],
|
||||
[Component.R3, Component.G3, Component.B3, Component.Y3, Component.C3, Component.M3, Component.W3],
|
||||
[Component.R4, Component.G4, Component.B4, Component.Y4, Component.C4, Component.M4, Component.W4],
|
||||
[Component.R5, Component.G5, Component.B5, Component.Y5, Component.C5, Component.M5, Component.W5],
|
||||
[Component.Y6, Component.C6, Component.M6, Component.W6],
|
||||
[Component.W7],
|
||||
];
|
||||
67
src/Myrian/formulas/costs.ts
Normal file
67
src/Myrian/formulas/costs.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { DeviceType } from "@enums";
|
||||
|
||||
// parameters for a exponential formula, a^(b*X+c)+d
|
||||
type ExponentialFormulaParams = [number, number, number, number];
|
||||
|
||||
// Parameters for a cost that shouldn't be available. Such as upgrading the max energy of a isocket.
|
||||
const NA: ExponentialFormulaParams = [Infinity, Infinity, Infinity, Infinity];
|
||||
|
||||
type DeviceScale = Record<DeviceType, ExponentialFormulaParams>;
|
||||
|
||||
// Default scale for each device type, helps simplify code.
|
||||
const defaultScale = Object.keys(DeviceType).reduce((acc, type) => ({ ...acc, [type]: NA }), {}) as DeviceScale;
|
||||
|
||||
// Exponential formula, a^(b*X+c)+d
|
||||
const exp = (p: ExponentialFormulaParams, x: number): number => Math.pow(p[0], p[1] * x + p[2]) + p[3];
|
||||
|
||||
// Wrap exp with a specific scale for each device type.
|
||||
const makeExpFunction = (p: Partial<DeviceScale>) => {
|
||||
const scale = { ...defaultScale, ...p };
|
||||
return (type: DeviceType, x: number) => exp(scale[type], x);
|
||||
};
|
||||
|
||||
export const upgradeMaxContentCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [8, 0.5, 2, 0],
|
||||
[DeviceType.ISocket]: [4, 1, 5, 0],
|
||||
[DeviceType.Reducer]: [256, 1, -3, 512],
|
||||
[DeviceType.Cache]: [1.2, 10, 0, 63],
|
||||
});
|
||||
|
||||
export const upgradeTierCost = makeExpFunction({
|
||||
[DeviceType.Reducer]: [1.5, 1, 2, 0],
|
||||
[DeviceType.Battery]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeEmissionCost = makeExpFunction({
|
||||
[DeviceType.ISocket]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeMoveLvlCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeTransferLvlCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeReduceLvlCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeInstallLvlCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeMaxEnergyCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [1.1, 1, -8, 16],
|
||||
[DeviceType.Battery]: [1.1, 1, -16, 8],
|
||||
});
|
||||
|
||||
export const installDeviceCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [4, 0.5, 2, 0],
|
||||
[DeviceType.ISocket]: [2, 1, 4, 0],
|
||||
[DeviceType.OSocket]: [4, 1, 3, 0],
|
||||
[DeviceType.Reducer]: [5, 0.5, 1, 0],
|
||||
[DeviceType.Cache]: [1.2, 5, 0, 18],
|
||||
[DeviceType.Battery]: [1.2, 10, 0, 63],
|
||||
});
|
||||
46
src/Myrian/formulas/glitches.ts
Normal file
46
src/Myrian/formulas/glitches.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Glitch } from "@enums";
|
||||
|
||||
export const glitchMaxLvl: Record<Glitch, number> = {
|
||||
[Glitch.Segmentation]: 10,
|
||||
[Glitch.Roaming]: 10,
|
||||
[Glitch.Encryption]: 7,
|
||||
[Glitch.Magnetism]: 10,
|
||||
[Glitch.Rust]: 10,
|
||||
[Glitch.Friction]: 3,
|
||||
[Glitch.Isolation]: 3,
|
||||
[Glitch.Virtualization]: 3,
|
||||
[Glitch.Jamming]: 3,
|
||||
};
|
||||
|
||||
export const giltchMultCoefficients: Record<Glitch, number> = {
|
||||
[Glitch.Segmentation]: 1,
|
||||
[Glitch.Roaming]: 1,
|
||||
[Glitch.Encryption]: 0.1,
|
||||
[Glitch.Magnetism]: 0.2,
|
||||
[Glitch.Rust]: 1,
|
||||
[Glitch.Friction]: 0.2,
|
||||
[Glitch.Isolation]: 0.2,
|
||||
[Glitch.Virtualization]: 0.2,
|
||||
[Glitch.Jamming]: 0.2,
|
||||
};
|
||||
|
||||
// vulns mult by glitch lvl
|
||||
export const glitchMult = (glitch: Glitch, lvl: number) => 1 + lvl * giltchMultCoefficients[glitch];
|
||||
|
||||
// move hinderance
|
||||
export const frictionMult = (lvl: number) => Math.pow(2.5, lvl);
|
||||
|
||||
// transfer slow down
|
||||
export const isolationMult = (lvl: number) => Math.pow(8, lvl);
|
||||
|
||||
// install/uninstall slow down
|
||||
export const virtualizationMult = (lvl: number) => Math.pow(5, lvl);
|
||||
|
||||
// reduce slow down
|
||||
export const jammingMult = (lvl: number) => Math.pow(2.5, lvl);
|
||||
|
||||
// energy loss
|
||||
export const magnetismLoss = (lvl: number) => lvl;
|
||||
|
||||
// How often isocket/osocke move
|
||||
export const roamingTime = (lvl: number) => 30000 * Math.pow(0.7, lvl);
|
||||
83
src/Myrian/formulas/recipes.ts
Normal file
83
src/Myrian/formulas/recipes.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Recipe } from "@nsdefs";
|
||||
import { Component } from "@enums";
|
||||
|
||||
const make = (input: Component[], output: Component): Recipe => ({ input, output });
|
||||
|
||||
export const Tier1Recipes: Recipe[] = [
|
||||
make([Component.R0, Component.R0], Component.R1),
|
||||
make([Component.G0, Component.G0], Component.G1),
|
||||
make([Component.B0, Component.B0], Component.B1),
|
||||
|
||||
make([Component.R0, Component.G0], Component.Y1),
|
||||
make([Component.G0, Component.B0], Component.C1),
|
||||
make([Component.B0, Component.R0], Component.M1),
|
||||
];
|
||||
|
||||
export const Tier2Recipes: Recipe[] = [
|
||||
make([Component.R1, Component.R1], Component.R2),
|
||||
make([Component.G1, Component.G1], Component.G2),
|
||||
make([Component.B1, Component.B1], Component.B2),
|
||||
|
||||
make([Component.R1, Component.G1], Component.Y2),
|
||||
make([Component.G1, Component.B1], Component.C2),
|
||||
make([Component.B1, Component.R1], Component.M2),
|
||||
|
||||
make([Component.Y1, Component.C1, Component.M1], Component.W2),
|
||||
];
|
||||
|
||||
export const Tier3Recipes: Recipe[] = [
|
||||
make([Component.R2, Component.R2], Component.R3),
|
||||
make([Component.G2, Component.G2], Component.G3),
|
||||
make([Component.B2, Component.B2], Component.B3),
|
||||
|
||||
make([Component.R2, Component.G2], Component.Y3),
|
||||
make([Component.G2, Component.B2], Component.C3),
|
||||
make([Component.B2, Component.R2], Component.M3),
|
||||
|
||||
make([Component.Y2, Component.C2, Component.M2], Component.W3),
|
||||
];
|
||||
|
||||
export const Tier4Recipes: Recipe[] = [
|
||||
make([Component.R3, Component.R3], Component.R4),
|
||||
make([Component.G3, Component.G3], Component.G4),
|
||||
make([Component.B3, Component.B3], Component.B4),
|
||||
|
||||
make([Component.R3, Component.G3], Component.Y4),
|
||||
make([Component.G3, Component.B3], Component.C4),
|
||||
make([Component.B3, Component.R3], Component.M4),
|
||||
|
||||
make([Component.Y3, Component.C3, Component.M3], Component.W4),
|
||||
];
|
||||
|
||||
export const Tier5Recipes: Recipe[] = [
|
||||
make([Component.R4, Component.R4], Component.R5),
|
||||
make([Component.G4, Component.G4], Component.G5),
|
||||
make([Component.B4, Component.B4], Component.B5),
|
||||
|
||||
make([Component.R4, Component.G4], Component.Y5),
|
||||
make([Component.G4, Component.B4], Component.C5),
|
||||
make([Component.B4, Component.R4], Component.M5),
|
||||
|
||||
make([Component.Y4, Component.C4, Component.M4], Component.W5),
|
||||
];
|
||||
|
||||
export const Tier6Recipes: Recipe[] = [
|
||||
make([Component.R5, Component.G5], Component.Y6),
|
||||
make([Component.G5, Component.B5], Component.C6),
|
||||
make([Component.B5, Component.R5], Component.M6),
|
||||
|
||||
make([Component.Y5, Component.C5, Component.M5], Component.W6),
|
||||
];
|
||||
|
||||
export const Tier7Recipes: Recipe[] = [make([Component.Y6, Component.C6, Component.M6], Component.W7)];
|
||||
|
||||
export const recipes: Recipe[][] = [
|
||||
[],
|
||||
Tier1Recipes,
|
||||
Tier2Recipes,
|
||||
Tier3Recipes,
|
||||
Tier4Recipes,
|
||||
Tier5Recipes,
|
||||
Tier6Recipes,
|
||||
Tier7Recipes,
|
||||
];
|
||||
14
src/Myrian/formulas/speed.ts
Normal file
14
src/Myrian/formulas/speed.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// speed to move between 2 tiles
|
||||
export const moveSpeed = (level: number) => 1000 / (level + 10);
|
||||
|
||||
// speed to reduce components
|
||||
export const reduceSpeed = (level: number) => 50000 / (level + 10);
|
||||
|
||||
// speed to transfer components between devices
|
||||
export const transferSpeed = (level: number) => 4000 / (level + 10);
|
||||
|
||||
// speed to install / uninstall devices and tweak ISockets
|
||||
export const installSpeed = (level: number) => 100000 / (level + 10);
|
||||
|
||||
// time until ISocket refreshes
|
||||
export const emissionSpeed = (level: number) => 100000 / (level + 10);
|
||||
12
src/Myrian/glitches/battery.ts
Normal file
12
src/Myrian/glitches/battery.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { myrian } from "../Myrian";
|
||||
import { isDeviceBattery } from "../utils";
|
||||
|
||||
const applyBattery = () => {
|
||||
myrian.devices.forEach((device) => {
|
||||
if (!isDeviceBattery(device)) return;
|
||||
const up = Math.pow(2, device.tier + 1);
|
||||
device.energy = Math.min(device.energy + up, device.maxEnergy);
|
||||
});
|
||||
};
|
||||
|
||||
export const startBattery = () => setInterval(applyBattery, 1000);
|
||||
74
src/Myrian/glitches/roaming.ts
Normal file
74
src/Myrian/glitches/roaming.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { DeviceType, Glitch } from "@enums";
|
||||
import { myrian, myrianSize } from "../Myrian";
|
||||
import { findDevice, inMyrianBounds } from "../Myrian";
|
||||
import { roamingTime } from "../formulas/glitches";
|
||||
|
||||
const clamp = (min: number, v: number, max: number) => Math.min(Math.max(v, min), max);
|
||||
|
||||
let globalOffset = 0;
|
||||
|
||||
const dirDiff = (v: number): number => {
|
||||
globalOffset++;
|
||||
const r = Math.random();
|
||||
const d = v - (myrianSize - 1) / 2;
|
||||
const h = d > 0 ? -1 : 1;
|
||||
|
||||
const dv = Math.floor(r * 3 + h * Math.random() * Math.sin(globalOffset * 0.05) * Math.abs(d)) - 1;
|
||||
return clamp(-1, dv, 1);
|
||||
};
|
||||
|
||||
const isEmpty = (x: number, y: number) => {
|
||||
if (!inMyrianBounds(x, y)) return false;
|
||||
return !findDevice([x, y]);
|
||||
};
|
||||
|
||||
const dirs = [
|
||||
[0, 1],
|
||||
[0, -1],
|
||||
[1, 0],
|
||||
[-1, 0],
|
||||
];
|
||||
|
||||
const applyRoaming = () => {
|
||||
const roaming = myrian.glitches[Glitch.Roaming];
|
||||
setTimeout(applyRoaming, roamingTime(roaming));
|
||||
if (roaming === 0) return;
|
||||
myrian.devices.forEach((device) => {
|
||||
if (device.type !== DeviceType.OSocket && device.type !== DeviceType.ISocket) return;
|
||||
if (device.isBusy) return;
|
||||
let canMove = false;
|
||||
for (const dir of dirs) {
|
||||
const [dx, dy] = dir;
|
||||
if (isEmpty(device.x + dx, device.y + dy)) {
|
||||
canMove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let x = -1;
|
||||
let y = -1;
|
||||
if (canMove) {
|
||||
let dx = dirDiff(device.x);
|
||||
let dy = dirDiff(device.y);
|
||||
|
||||
if (dx !== 0 && dy !== 0) {
|
||||
if (Math.random() > 0.5) {
|
||||
dx = 0;
|
||||
} else {
|
||||
dy = 0;
|
||||
}
|
||||
}
|
||||
x = device.x + dx;
|
||||
y = device.y + dy;
|
||||
} else {
|
||||
x = Math.floor(Math.random() * myrianSize);
|
||||
y = Math.floor(Math.random() * myrianSize);
|
||||
}
|
||||
|
||||
if (findDevice([x, y])) return;
|
||||
if (!inMyrianBounds(x, y)) return;
|
||||
device.x = x;
|
||||
device.y = y;
|
||||
});
|
||||
};
|
||||
|
||||
export const startRoaming = () => setTimeout(applyRoaming, 0);
|
||||
30
src/Myrian/glitches/rust.ts
Normal file
30
src/Myrian/glitches/rust.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Bus } from "@nsdefs";
|
||||
import { Glitch } from "@enums";
|
||||
import { myrian, myrianSize } from "../Myrian";
|
||||
import { pickOne } from "../utils";
|
||||
|
||||
const applyRust = () => {
|
||||
myrian.rust = {};
|
||||
const rust = myrian.glitches[Glitch.Rust];
|
||||
for (let i = 0; i < rust * 3; i++) {
|
||||
const x = Math.floor(Math.random() * myrianSize);
|
||||
const y = Math.floor(Math.random() * myrianSize);
|
||||
myrian.rust[`${x}:${y}`] = true;
|
||||
}
|
||||
};
|
||||
|
||||
// DO NOT use `Object.keys` on a Rustable because it will return way more than just the rustable stats.
|
||||
const rustStats: (keyof Rustable)[] = ["moveLvl", "transferLvl", "reduceLvl", "installLvl", "maxEnergy"];
|
||||
type Rustable = Pick<Bus, "moveLvl" | "transferLvl" | "reduceLvl" | "installLvl" | "maxEnergy">;
|
||||
|
||||
export const rustBus = (bus: Bus, rust: number) => {
|
||||
const rustable = bus as Rustable;
|
||||
const nonZero = rustStats.filter((stat) => rustable[stat] > 0);
|
||||
const chosen = pickOne(nonZero);
|
||||
rustable[chosen] = Math.max(0, rustable[chosen] - rust * 0.1);
|
||||
|
||||
// cap energy when maxEnergy is reduced
|
||||
bus.energy = Math.min(bus.energy, bus.maxEnergy);
|
||||
};
|
||||
|
||||
export const startRust = () => setInterval(applyRust, 30000);
|
||||
16
src/Myrian/glitches/segmentation.ts
Normal file
16
src/Myrian/glitches/segmentation.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Glitch } from "@enums";
|
||||
import { myrian, myrianSize } from "../Myrian";
|
||||
import { findDevice } from "../Myrian";
|
||||
import { NewLock } from "../NewDevices";
|
||||
|
||||
const applySegmentation = () => {
|
||||
const segmentation = myrian.glitches[Glitch.Segmentation];
|
||||
for (let i = 0; i < segmentation; i++) {
|
||||
const x = Math.floor(Math.random() * myrianSize);
|
||||
const y = Math.floor(Math.random() * myrianSize);
|
||||
if (findDevice([x, y])) continue;
|
||||
NewLock(`lock-${x}-${y}`, x, y);
|
||||
}
|
||||
};
|
||||
|
||||
export const startSegmentation = () => setInterval(applySegmentation, 30000);
|
||||
1
src/Myrian/ideas.txt
Normal file
1
src/Myrian/ideas.txt
Normal file
@@ -0,0 +1 @@
|
||||
New device that makes special requests like "has red" | "at least tier 3" | "does not contain blue" that increases the reward.
|
||||
22
src/Myrian/ui/BatteryIcon.tsx
Normal file
22
src/Myrian/ui/BatteryIcon.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import { Battery } from "@nsdefs";
|
||||
import BatteryChargingFullIcon from "@mui/icons-material/BatteryChargingFull";
|
||||
import { defaultIconStyle } from "./common";
|
||||
import { styled } from "@mui/styles";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
import { TooltipTier } from "./TooltipTier";
|
||||
import { TooltipEnergy } from "./TooltipEnergy";
|
||||
|
||||
const Template = styled(BatteryChargingFullIcon)(defaultIconStyle);
|
||||
const Icon = <Template />;
|
||||
|
||||
interface IBatteryIconProps {
|
||||
battery: Battery;
|
||||
}
|
||||
|
||||
export const BatteryIcon = ({ battery }: IBatteryIconProps): React.ReactElement => (
|
||||
<DeviceTooltip device={battery} icon={Icon}>
|
||||
<TooltipTier device={battery} />
|
||||
<TooltipEnergy device={battery} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
27
src/Myrian/ui/BusIcon.tsx
Normal file
27
src/Myrian/ui/BusIcon.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
import { defaultIconStyle } from "./common";
|
||||
import DirectionsBusIcon from "@mui/icons-material/DirectionsBus";
|
||||
import { styled } from "@mui/styles";
|
||||
import { Bus } from "@nsdefs";
|
||||
import { TooltipContent } from "./TooltipContent";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
import { TooltipEnergy } from "./TooltipEnergy";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
const Template = styled(DirectionsBusIcon)(defaultIconStyle);
|
||||
const Icon = <Template />;
|
||||
|
||||
interface IBusIconProps {
|
||||
bus: Bus;
|
||||
}
|
||||
|
||||
export const BusIcon = ({ bus }: IBusIconProps): React.ReactElement => (
|
||||
<DeviceTooltip device={bus} icon={Icon}>
|
||||
<Typography>moveLvl: {bus.moveLvl}</Typography>
|
||||
<Typography>transferLvl: {bus.transferLvl}</Typography>
|
||||
<Typography>reduceLvl: {bus.reduceLvl}</Typography>
|
||||
<Typography>installLvl: {bus.installLvl}</Typography>
|
||||
<TooltipEnergy device={bus} />
|
||||
<TooltipContent device={bus} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
20
src/Myrian/ui/CacheIcon.tsx
Normal file
20
src/Myrian/ui/CacheIcon.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import { TooltipContent } from "./TooltipContent";
|
||||
import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank";
|
||||
import { styled } from "@mui/styles";
|
||||
import { defaultIconStyle } from "./common";
|
||||
import { Cache } from "@nsdefs";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
|
||||
const Template = styled(CheckBoxOutlineBlankIcon)(defaultIconStyle);
|
||||
const Icon = <Template />;
|
||||
|
||||
interface ICacheIconProps {
|
||||
cache: Cache;
|
||||
}
|
||||
|
||||
export const CacheIcon = ({ cache }: ICacheIconProps): React.ReactElement => (
|
||||
<DeviceTooltip device={cache} icon={Icon}>
|
||||
<TooltipContent device={cache} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
14
src/Myrian/ui/ComponentText.tsx
Normal file
14
src/Myrian/ui/ComponentText.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
import { Component } from "@enums";
|
||||
import { getComponentColor } from "./common";
|
||||
|
||||
interface IComponent {
|
||||
component: Component;
|
||||
}
|
||||
|
||||
export const ComponentText = ({ component }: IComponent): React.ReactElement => (
|
||||
<span style={{ color: getComponentColor(component) }}>
|
||||
{component}
|
||||
<br />
|
||||
</span>
|
||||
);
|
||||
35
src/Myrian/ui/DeviceIcon.tsx
Normal file
35
src/Myrian/ui/DeviceIcon.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from "react";
|
||||
import { Device } from "@nsdefs";
|
||||
import { DeviceType } from "@enums";
|
||||
|
||||
import { BusIcon } from "./BusIcon";
|
||||
import { ReducerIcon } from "./Reducer";
|
||||
import { LockIcon } from "./LockIcon";
|
||||
import { CacheIcon } from "./CacheIcon";
|
||||
import { BatteryIcon } from "./BatteryIcon";
|
||||
import { OSocketIcon } from "./OSocketIcon";
|
||||
import { ISocketIcon } from "./ISocketIcon";
|
||||
import {
|
||||
isDeviceBattery,
|
||||
isDeviceBus,
|
||||
isDeviceCache,
|
||||
isDeviceISocket,
|
||||
isDeviceLock,
|
||||
isDeviceOSocket,
|
||||
isDeviceReducer,
|
||||
} from "../utils";
|
||||
|
||||
interface IDeviceIconProps {
|
||||
device: Device;
|
||||
}
|
||||
|
||||
export const DeviceIcon = ({ device }: IDeviceIconProps): React.ReactElement => {
|
||||
if (isDeviceISocket(device)) return <ISocketIcon socket={device} />;
|
||||
if (isDeviceOSocket(device)) return <OSocketIcon socket={device} />;
|
||||
if (isDeviceBus(device)) return <BusIcon bus={device} />;
|
||||
if (isDeviceReducer(device)) return <ReducerIcon reducer={device} />;
|
||||
if (isDeviceLock(device)) return <LockIcon lock={device} />;
|
||||
if (isDeviceCache(device)) return <CacheIcon cache={device} />;
|
||||
if (isDeviceBattery(device)) return <BatteryIcon battery={device} />;
|
||||
return <></>;
|
||||
};
|
||||
24
src/Myrian/ui/DeviceTooltip.tsx
Normal file
24
src/Myrian/ui/DeviceTooltip.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { Device } from "../../ScriptEditor/NetscriptDefinitions";
|
||||
import { Tooltip, Typography } from "@mui/material";
|
||||
|
||||
interface INewTooltipProps {
|
||||
icon: React.JSX.Element;
|
||||
device: Device;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export const DeviceTooltip = ({ device, icon, children }: INewTooltipProps): React.ReactElement => (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography>
|
||||
{device.name} ({device.type})
|
||||
</Typography>
|
||||
{children}
|
||||
</>
|
||||
}
|
||||
>
|
||||
{icon}
|
||||
</Tooltip>
|
||||
);
|
||||
88
src/Myrian/ui/Grid.tsx
Normal file
88
src/Myrian/ui/Grid.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import React from "react";
|
||||
import { cellSize } from "./common";
|
||||
import { styled } from "@mui/system";
|
||||
import { findDevice, myrianSize } from "../Myrian";
|
||||
import { DeviceIcon } from "./DeviceIcon";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
const BaseCell = styled("div")({
|
||||
width: cellSize,
|
||||
height: cellSize,
|
||||
backgroundColor: "#444",
|
||||
padding: 0,
|
||||
margin: "2px",
|
||||
marginTop: "4px",
|
||||
marginBottom: "4px",
|
||||
});
|
||||
|
||||
const TextCell = styled(BaseCell)({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "#00000000",
|
||||
});
|
||||
|
||||
const DeviceCell = ({ x, y }: { x: number; y: number }): React.ReactElement => {
|
||||
const device = findDevice([x, y]);
|
||||
return <BaseCell>{device && <DeviceIcon device={device} />}</BaseCell>;
|
||||
};
|
||||
|
||||
const ColD = styled("div")({
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
});
|
||||
|
||||
interface IColProps {
|
||||
x: number;
|
||||
}
|
||||
|
||||
const DeviceCol = ({ x }: IColProps): React.ReactElement => {
|
||||
return (
|
||||
<ColD>
|
||||
<TextCell>
|
||||
<Typography>{x}</Typography>
|
||||
</TextCell>
|
||||
{new Array(myrianSize).fill(undefined).map((_, y) => (
|
||||
<DeviceCell key={y} x={x} y={y}></DeviceCell>
|
||||
))}
|
||||
</ColD>
|
||||
);
|
||||
};
|
||||
|
||||
const Table = styled("div")({
|
||||
border: "1px solid #fff",
|
||||
borderSpacing: "2px",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
paddingLeft: "2px",
|
||||
paddingRight: "2px",
|
||||
});
|
||||
|
||||
const YColumn = (
|
||||
<ColD>
|
||||
<TextCell>
|
||||
<Typography>
|
||||
X
|
||||
<br />
|
||||
Y
|
||||
</Typography>
|
||||
</TextCell>
|
||||
{new Array(myrianSize).fill(undefined).map((_, y) => (
|
||||
<TextCell key={y}>
|
||||
<Typography>{y}</Typography>
|
||||
</TextCell>
|
||||
))}
|
||||
</ColD>
|
||||
);
|
||||
|
||||
export const Grid = () => (
|
||||
<div style={{ display: "flex" }}>
|
||||
<Table>
|
||||
{YColumn}
|
||||
{new Array(myrianSize).fill(undefined).map((_, x) => (
|
||||
<DeviceCol key={x} x={x} />
|
||||
))}
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
33
src/Myrian/ui/ISocketIcon.tsx
Normal file
33
src/Myrian/ui/ISocketIcon.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { TooltipContent } from "./TooltipContent";
|
||||
import { ISocket } from "../../ScriptEditor/NetscriptDefinitions";
|
||||
import { defaultIconStyle, getComponentColor } from "./common";
|
||||
import OutboxIcon from "@mui/icons-material/Outbox";
|
||||
import { styled } from "@mui/styles";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
import { ComponentText } from "./ComponentText";
|
||||
|
||||
interface IIconProps {
|
||||
socket: ISocket;
|
||||
col: string;
|
||||
}
|
||||
|
||||
const Icon = styled(OutboxIcon)(({ socket, col }: IIconProps) => ({
|
||||
...defaultIconStyle,
|
||||
color: new Date().getTime() > socket.cooldownUntil ? col : "gray",
|
||||
}));
|
||||
|
||||
interface IIsocketIconProps {
|
||||
socket: ISocket;
|
||||
}
|
||||
|
||||
export const ISocketIcon = ({ socket }: IIsocketIconProps) => (
|
||||
<DeviceTooltip device={socket} icon={<Icon socket={socket} col={getComponentColor(socket.emitting)} />}>
|
||||
<Typography>
|
||||
dispensing:
|
||||
<ComponentText component={socket.emitting} />
|
||||
</Typography>
|
||||
<TooltipContent device={socket} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
15
src/Myrian/ui/LockIcon.tsx
Normal file
15
src/Myrian/ui/LockIcon.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import BlockIcon from "@mui/icons-material/Block";
|
||||
import { styled } from "@mui/styles";
|
||||
import { defaultIconStyle } from "./common";
|
||||
import { Lock } from "@nsdefs";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
|
||||
const Template = styled(BlockIcon)(defaultIconStyle);
|
||||
const Icon = <Template />;
|
||||
|
||||
interface ILockIconProps {
|
||||
lock: Lock;
|
||||
}
|
||||
|
||||
export const LockIcon = ({ lock }: ILockIconProps): React.ReactElement => <DeviceTooltip device={lock} icon={Icon} />;
|
||||
32
src/Myrian/ui/MyrianRoot.tsx
Normal file
32
src/Myrian/ui/MyrianRoot.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from "react";
|
||||
import { Container, IconButton, Typography } from "@mui/material";
|
||||
|
||||
import { myrian } from "../Myrian";
|
||||
import { useRerender } from "../../ui/React/hooks";
|
||||
import { Info } from "@mui/icons-material";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { MD } from "../../ui/MD/MD";
|
||||
import { tutorial } from "./tutorial";
|
||||
import { Grid } from "./Grid";
|
||||
|
||||
const tut = <MD md={tutorial} />;
|
||||
|
||||
export const MyrianRoot = (): React.ReactElement => {
|
||||
useRerender(50);
|
||||
|
||||
const onHelp = () => dialogBoxCreate(tut);
|
||||
return (
|
||||
<Container maxWidth="lg" disableGutters sx={{ mx: 0 }}>
|
||||
<Typography variant="h4">
|
||||
Myrian OS
|
||||
<IconButton onClick={onHelp}>
|
||||
<Info />
|
||||
</IconButton>
|
||||
</Typography>
|
||||
<Typography>
|
||||
{myrian.vulns} vulns : {myrian.totalVulns} total vulns
|
||||
</Typography>
|
||||
<Grid />
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
35
src/Myrian/ui/OSocketIcon.tsx
Normal file
35
src/Myrian/ui/OSocketIcon.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from "react";
|
||||
import { Typography } from "@mui/material";
|
||||
import { OSocket } from "@nsdefs";
|
||||
import { defaultIconStyle, getComponentColor } from "./common";
|
||||
import MoveToInboxIcon from "@mui/icons-material/MoveToInbox";
|
||||
import { styled } from "@mui/styles";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
import { TooltipContent } from "./TooltipContent";
|
||||
import { ComponentText } from "./ComponentText";
|
||||
|
||||
interface IIconProps {
|
||||
col: string;
|
||||
}
|
||||
|
||||
const Icon = styled(MoveToInboxIcon)(({ col }: IIconProps) => ({
|
||||
...defaultIconStyle,
|
||||
color: col,
|
||||
}));
|
||||
|
||||
interface IOSocketIconProps {
|
||||
socket: OSocket;
|
||||
}
|
||||
|
||||
export const OSocketIcon = ({ socket }: IOSocketIconProps): React.ReactElement => (
|
||||
<DeviceTooltip device={socket} icon={<Icon col={getComponentColor(socket.currentRequest[0])} />}>
|
||||
<Typography>
|
||||
requesting:
|
||||
<br />
|
||||
{socket.currentRequest.map((c, i) => (
|
||||
<ComponentText key={i} component={c} />
|
||||
))}
|
||||
</Typography>
|
||||
<TooltipContent device={socket} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
22
src/Myrian/ui/Reducer.tsx
Normal file
22
src/Myrian/ui/Reducer.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import { TooltipContent } from "./TooltipContent";
|
||||
import { Reducer } from "@nsdefs";
|
||||
import MergeTypeIcon from "@mui/icons-material/MergeType";
|
||||
import { styled } from "@mui/styles";
|
||||
import { defaultIconStyle } from "./common";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
import { TooltipTier } from "./TooltipTier";
|
||||
|
||||
const Template = styled(MergeTypeIcon)(defaultIconStyle);
|
||||
const Icon = <Template />;
|
||||
|
||||
interface IReducerIconProps {
|
||||
reducer: Reducer;
|
||||
}
|
||||
|
||||
export const ReducerIcon = ({ reducer }: IReducerIconProps): React.ReactElement => (
|
||||
<DeviceTooltip device={reducer} icon={Icon}>
|
||||
<TooltipTier device={reducer} />
|
||||
<TooltipContent device={reducer} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
18
src/Myrian/ui/TooltipContent.tsx
Normal file
18
src/Myrian/ui/TooltipContent.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
import { ContainerDevice } from "@nsdefs";
|
||||
import { Typography } from "@mui/material";
|
||||
import { ComponentText } from "./ComponentText";
|
||||
|
||||
export const TooltipContent = ({ device }: { device: ContainerDevice }): React.ReactElement => (
|
||||
<>
|
||||
{device.content.length !== 0 && (
|
||||
<Typography>
|
||||
content ({device.content.length} / {device.maxContent}):
|
||||
<br />
|
||||
{device.content.map((component, i) => (
|
||||
<ComponentText key={i} component={component} />
|
||||
))}
|
||||
</Typography>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
13
src/Myrian/ui/TooltipEnergy.tsx
Normal file
13
src/Myrian/ui/TooltipEnergy.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
import { EnergyDevice } from "@nsdefs";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
interface ITooltipEnergyProps {
|
||||
device: EnergyDevice;
|
||||
}
|
||||
|
||||
export const TooltipEnergy = ({ device }: ITooltipEnergyProps): React.ReactElement => (
|
||||
<Typography>
|
||||
{device.energy} / {device.maxEnergy} energy
|
||||
</Typography>
|
||||
);
|
||||
11
src/Myrian/ui/TooltipTier.tsx
Normal file
11
src/Myrian/ui/TooltipTier.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from "react";
|
||||
import { TieredDevice } from "@nsdefs";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
interface ITooltipTierProps {
|
||||
device: TieredDevice;
|
||||
}
|
||||
|
||||
export const TooltipTier = ({ device }: ITooltipTierProps): React.ReactElement => (
|
||||
<Typography>Tier: {device.tier}</Typography>
|
||||
);
|
||||
30
src/Myrian/ui/common.ts
Normal file
30
src/Myrian/ui/common.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Component } from "@enums";
|
||||
|
||||
export const cellSize = 48;
|
||||
|
||||
export const defaultIconStyle = {
|
||||
width: cellSize + "px",
|
||||
height: cellSize + "px",
|
||||
color: "white",
|
||||
};
|
||||
|
||||
const ColorR = "red";
|
||||
const ColorG = "#7CFC00";
|
||||
const ColorB = "#1E90FF";
|
||||
const ColorY = "yellow";
|
||||
const ColorC = "cyan";
|
||||
const ColorM = "magenta";
|
||||
const ColorW = "white";
|
||||
|
||||
const componentColors: Record<string, string> = {
|
||||
r: ColorR,
|
||||
g: ColorG,
|
||||
b: ColorB,
|
||||
y: ColorY,
|
||||
c: ColorC,
|
||||
m: ColorM,
|
||||
w: ColorW,
|
||||
};
|
||||
|
||||
export const getComponentColor = (component: Component): string =>
|
||||
componentColors[component[0].toLowerCase()] ?? ColorW;
|
||||
109
src/Myrian/ui/tutorial.ts
Normal file
109
src/Myrian/ui/tutorial.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
export const tutorial = `# Myrian
|
||||
|
||||
Myrian is the name of the OS that the BitNodes run on.
|
||||
|
||||
By gaining access to the OS directly you can start to break it apart by generating vulnerabilities (vulns).
|
||||
You do so by interracting with the various devices in the OS, represented by a grid.
|
||||
|
||||
## Devices
|
||||
|
||||
A device is a component that takes space on the Myrian, no 2 devices can be on the same tile.
|
||||
Devices can only interract when directly adjacent to each other. When one or more device is
|
||||
performing an action it will become busy and no other actions can be taken until the current one finishes.
|
||||
|
||||
Contrary to every other mechanic in the game. Async functions using myrian functions CAN run simultenaously.
|
||||
|
||||
|
||||
### Bus
|
||||
|
||||
The most important device is the Bus. Here's a few of the things it can do:
|
||||
|
||||
- move, only 1 tile at a time and that tile must be empty. No diagonal.
|
||||
- transfer content, most entity can store items called components, bus are the only device that can transfer components to and from other devices
|
||||
- use other devices, e.g. Use a Reducer device to create new components.
|
||||
|
||||
### ISocket
|
||||
|
||||
These devices produce basic components that can be used for other devices, [r0, g0, b0].
|
||||
They must be picked up by busses and will eventually produce another one after a certain cooldown has passed.
|
||||
|
||||
### OSocket
|
||||
|
||||
These devices request components and produce vulns in return, a bus simply needs to transfer a component into a OSocket content in order to fulfill the request
|
||||
|
||||
### Reducer
|
||||
|
||||
These devices can be used by a bus, when being used they will first check their content, if the content matches one of the recipe they will take some time to consume their content in order to produce a new, upgraded, more valuable component, e.g. r0 + r0 => r1
|
||||
|
||||
### Cache
|
||||
|
||||
These devices act as storage for components.
|
||||
|
||||
### Lock
|
||||
|
||||
These devices cannot be installed. They appear after various conditions are fulfilled in order to block certain tiles.
|
||||
|
||||
### Battery
|
||||
|
||||
These devices are only relevant when the Magnetism glitch is active. It recharges the energy of a bus.
|
||||
|
||||
## Installing
|
||||
|
||||
Bus can install new devices, when they do so a lock will appear over the tile that will eventually become the device. The cost of any device depends on the number of that type of device currently in the OS.
|
||||
|
||||
### Uninstalling
|
||||
|
||||
A bus can remove a device, there is no refund.
|
||||
|
||||
## Tiers
|
||||
|
||||
Upgrading a reducers tier allows it to reduce components of a higher tier and ONLY that higher tier.
|
||||
A tier 2 reducer can only tier 2 components like r1 + r1 => r2 and loses access to r0 + r0 => r1
|
||||
|
||||
Upgrading a batterys tier increases the amount of energy it produces.
|
||||
|
||||
## Glitches
|
||||
|
||||
glitches are optional difficulty modifiers that make the myrian more difficult BUT increase the amount of vulns gained.
|
||||
All glitches start at level 0 and must be activated when you chose. They also have a max level that differs from glitch to glitch.
|
||||
|
||||
### Magnetism
|
||||
|
||||
By default bus lose 0 energy when moving. But when this glitch is active they start losing energy, at 0 energy bus move much more slowly. Batteries must be installed and used to charge busses.
|
||||
|
||||
### Friction
|
||||
|
||||
When Friction is active busses move more slowly.
|
||||
|
||||
### Rust
|
||||
|
||||
When rust is active, hidden tiles are set on the Myrian. When a bus steps on one of these hidden tiles one of their upgrade lowers. Higher Rust level means lowers by a larger amount and more rust tiles are set.
|
||||
|
||||
### Isolation
|
||||
|
||||
When Isolation is active busses transfer and charge more slowly.
|
||||
|
||||
### Segmentation
|
||||
|
||||
When Segmentation is active random Locks will spawn on the Myrian. You have to remove these locks or the bord will be overrun with Locks and you won't be able to move.
|
||||
|
||||
### Virtualization
|
||||
|
||||
When Virtualization is active busses install and uninstall devices more slowly.
|
||||
|
||||
### Jamming
|
||||
|
||||
When Jamming is active busses use reducers more slowly.
|
||||
|
||||
### Roaming
|
||||
|
||||
When Roaming is active, isockets and osockets start to move around the map
|
||||
|
||||
### Encryption
|
||||
|
||||
Encryption is the only glitch that's always active. The level of Encryption determines the complexity of the requests made by osockets.
|
||||
|
||||
## Destabilization
|
||||
|
||||
As the number of total vulns increase the bitnode becomes unstable and it's multiplier become more favorable.
|
||||
`;
|
||||
125
src/Myrian/utils.ts
Normal file
125
src/Myrian/utils.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import {
|
||||
BaseDevice,
|
||||
ContainerDevice,
|
||||
Device,
|
||||
Bus,
|
||||
ISocket,
|
||||
OSocket,
|
||||
Reducer,
|
||||
Cache,
|
||||
Lock,
|
||||
Battery,
|
||||
TieredDevice,
|
||||
EnergyDevice,
|
||||
} from "@nsdefs";
|
||||
import { Component, DeviceType } from "@enums";
|
||||
|
||||
export const pickOne = <T>(arr: T[]): T => arr[Math.floor(Math.random() * arr.length)];
|
||||
|
||||
export const distance = (a: Device, b: Device) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
||||
export const distanceCoord2D = (a: Device, coord: [number, number]) =>
|
||||
Math.abs(a.x - coord[0]) + Math.abs(a.y - coord[1]);
|
||||
|
||||
export const adjacent = (a: Device, b: Device) => distance(a, b) === 1;
|
||||
export const adjacentCoord2D = (a: Device, coord: [number, number]) => distanceCoord2D(a, coord) === 1;
|
||||
|
||||
export const makeContentMap = (content: Component[]) =>
|
||||
content.reduce((acc, c) => ({ ...acc, [c]: (acc[c] ?? 0) + 1 }), {} as Record<Component, number>);
|
||||
|
||||
export const inventoryMatches = (a: Component[], b: Component[]) => {
|
||||
const aMap = makeContentMap(a);
|
||||
const bMap = makeContentMap(b);
|
||||
|
||||
return (
|
||||
(Object.keys(aMap) as Component[]).every((k) => aMap[k] === bMap[k]) &&
|
||||
Object.keys(aMap).length === Object.keys(aMap).length
|
||||
);
|
||||
};
|
||||
|
||||
const vulnsMap: Record<Component, number> = {
|
||||
// tier 0
|
||||
[Component.R0]: 1,
|
||||
[Component.G0]: 1,
|
||||
[Component.B0]: 1,
|
||||
|
||||
// tier 1
|
||||
[Component.R1]: 4,
|
||||
[Component.G1]: 4,
|
||||
[Component.B1]: 4,
|
||||
|
||||
[Component.Y1]: 4,
|
||||
[Component.C1]: 4,
|
||||
[Component.M1]: 4,
|
||||
|
||||
// tier 2
|
||||
[Component.R2]: 16,
|
||||
[Component.G2]: 16,
|
||||
[Component.B2]: 16,
|
||||
|
||||
[Component.Y2]: 16,
|
||||
[Component.C2]: 16,
|
||||
[Component.M2]: 16,
|
||||
|
||||
[Component.W2]: 16,
|
||||
|
||||
// tier 3
|
||||
[Component.R3]: 64,
|
||||
[Component.G3]: 64,
|
||||
[Component.B3]: 64,
|
||||
|
||||
[Component.Y3]: 64,
|
||||
[Component.C3]: 64,
|
||||
[Component.M3]: 64,
|
||||
|
||||
[Component.W3]: 64,
|
||||
|
||||
// tier 4
|
||||
[Component.R4]: 256,
|
||||
[Component.G4]: 256,
|
||||
[Component.B4]: 256,
|
||||
|
||||
[Component.Y4]: 256,
|
||||
[Component.C4]: 256,
|
||||
[Component.M4]: 256,
|
||||
|
||||
[Component.W4]: 256,
|
||||
|
||||
// tier 5
|
||||
[Component.R5]: 1024,
|
||||
[Component.G5]: 1024,
|
||||
[Component.B5]: 1024,
|
||||
|
||||
[Component.Y5]: 1024,
|
||||
[Component.C5]: 1024,
|
||||
[Component.M5]: 1024,
|
||||
|
||||
[Component.W5]: 1024,
|
||||
|
||||
// tier 6
|
||||
[Component.Y6]: 4096,
|
||||
[Component.C6]: 4096,
|
||||
[Component.M6]: 4096,
|
||||
|
||||
[Component.W6]: 4096,
|
||||
|
||||
// tier 7
|
||||
[Component.W7]: 16384,
|
||||
};
|
||||
|
||||
export const contentVulnsValue = (content: Component[]) => content.map((i) => vulnsMap[i]).reduce((a, b) => a + b, 0);
|
||||
|
||||
export const isDeviceContainer = (device: BaseDevice): device is ContainerDevice => "content" in device;
|
||||
export const isDeviceBus = (d: Device): d is Bus => d.type === DeviceType.Bus;
|
||||
export const isDeviceISocket = (d: Device): d is ISocket => d.type === DeviceType.ISocket;
|
||||
export const isDeviceOSocket = (d: Device): d is OSocket => d.type === DeviceType.OSocket;
|
||||
export const isDeviceReducer = (d: Device): d is Reducer => d.type === DeviceType.Reducer;
|
||||
export const isDeviceCache = (d: Device): d is Cache => d.type === DeviceType.Cache;
|
||||
export const isDeviceLock = (d: Device): d is Lock => d.type === DeviceType.Lock;
|
||||
export const isDeviceBattery = (d: Device): d is Battery => d.type === DeviceType.Battery;
|
||||
export const isDeviceTiered = (d: BaseDevice): d is TieredDevice => "tier" in d;
|
||||
export const isEmittingDevice = (d: BaseDevice): d is BaseDevice & { emissionLvl: number } => "emissionLvl" in d;
|
||||
export const isMovingDevice = (d: BaseDevice): d is BaseDevice & { moveLvl: number } => "moveLvl" in d;
|
||||
export const isTransferingDevice = (d: BaseDevice): d is BaseDevice & { transferLvl: number } => "transferLvl" in d;
|
||||
export const isReducingDevice = (d: BaseDevice): d is BaseDevice & { reduceLvl: number } => "reduceLvl" in d;
|
||||
export const isInstallingDevice = (d: BaseDevice): d is BaseDevice & { installLvl: number } => "installLvl" in d;
|
||||
export const isEnergyDevice = (d: BaseDevice): d is EnergyDevice => "maxEnergy" in d;
|
||||
@@ -1,5 +1,11 @@
|
||||
import type { NetscriptContext } from "./APIWrapper";
|
||||
import type { RunningScript as IRunningScript, Person as IPerson, Server as IServer, ScriptArg } from "@nsdefs";
|
||||
import type {
|
||||
RunningScript as IRunningScript,
|
||||
Person as IPerson,
|
||||
Server as IServer,
|
||||
ScriptArg,
|
||||
DeviceID,
|
||||
} from "@nsdefs";
|
||||
|
||||
import React from "react";
|
||||
import { killWorkerScript } from "./killWorkerScript";
|
||||
@@ -74,6 +80,8 @@ export const helpers = {
|
||||
gangMember,
|
||||
gangTask,
|
||||
log,
|
||||
coord2d,
|
||||
deviceID: entityID,
|
||||
filePath,
|
||||
scriptPath,
|
||||
getRunningScript,
|
||||
@@ -152,6 +160,24 @@ function scriptArgs(ctx: NetscriptContext, args: unknown) {
|
||||
return args;
|
||||
}
|
||||
|
||||
function isCoord2D(v: unknown): v is [number, number] {
|
||||
return Array.isArray(v) && v.length === 2 && typeof v[0] === "number" && typeof v[1] === "number";
|
||||
}
|
||||
|
||||
function coord2d(ctx: NetscriptContext, argName: string, v: unknown): [number, number] {
|
||||
if (!isCoord2D(v)) throw errorMessage(ctx, `${argName} should be a [number, number], was ${v}`, "TYPE");
|
||||
return v;
|
||||
}
|
||||
|
||||
function isEntityID(v: unknown): v is DeviceID {
|
||||
return typeof v === "string" || isCoord2D(v);
|
||||
}
|
||||
|
||||
function entityID(ctx: NetscriptContext, argName: string, v: unknown): DeviceID {
|
||||
if (!isEntityID(v)) throw errorMessage(ctx, `${argName} should be string | [number, number], was ${v}`, "TYPE");
|
||||
return v;
|
||||
}
|
||||
|
||||
function runOptions(ctx: NetscriptContext, threadOrOption: unknown): CompleteRunOptions {
|
||||
const result: CompleteRunOptions = {
|
||||
threads: 1 as PositiveInteger,
|
||||
@@ -302,7 +328,7 @@ function checkEnvFlags(ctx: NetscriptContext): void {
|
||||
}
|
||||
|
||||
/** Set a timeout for performing a task, mark the script as busy in the meantime. */
|
||||
function netscriptDelay(ctx: NetscriptContext, time: number): Promise<void> {
|
||||
function netscriptDelay(ctx: NetscriptContext, time: number, ignoreOthers?: boolean): Promise<void> {
|
||||
const ws = ctx.workerScript;
|
||||
return new Promise(function (resolve, reject) {
|
||||
ws.delay = window.setTimeout(() => {
|
||||
@@ -313,7 +339,7 @@ function netscriptDelay(ctx: NetscriptContext, time: number): Promise<void> {
|
||||
else resolve();
|
||||
}, time);
|
||||
ws.delayReject = reject;
|
||||
ws.env.runningFn = ctx.function;
|
||||
if (ignoreOthers) ws.env.runningFn = ctx.function;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -368,6 +368,15 @@ const stanek = {
|
||||
acceptGift: RamCostConstants.StanekAcceptGift,
|
||||
} as const;
|
||||
|
||||
const myrian: any = new Proxy(
|
||||
{},
|
||||
{
|
||||
get() {
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// UI API
|
||||
const ui = {
|
||||
getTheme: 0,
|
||||
@@ -470,6 +479,7 @@ export const RamCosts: RamCostTree<NSFull> = {
|
||||
codingcontract,
|
||||
sleeve,
|
||||
stanek,
|
||||
myrian,
|
||||
ui,
|
||||
grafting,
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ import { getEnumHelper } from "./utils/EnumHelper";
|
||||
import { setDeprecatedProperties, deprecationWarning } from "./utils/DeprecationHelper";
|
||||
import { ServerConstants } from "./Server/data/Constants";
|
||||
import { assertFunction } from "./Netscript/TypeAssertion";
|
||||
import { NetscriptMyrian } from "./NetscriptFunctions/Myrian";
|
||||
|
||||
export const enums: NSEnums = {
|
||||
CityName,
|
||||
@@ -135,6 +136,7 @@ export const ns: InternalAPI<NSFull> = {
|
||||
sleeve: NetscriptSleeve(),
|
||||
corporation: NetscriptCorporation(),
|
||||
stanek: NetscriptStanek(),
|
||||
myrian: NetscriptMyrian(),
|
||||
infiltration: NetscriptInfiltration(),
|
||||
ui: NetscriptUserInterface(),
|
||||
formulas: NetscriptFormulas(),
|
||||
|
||||
751
src/NetscriptFunctions/Myrian.ts
Normal file
751
src/NetscriptFunctions/Myrian.ts
Normal file
@@ -0,0 +1,751 @@
|
||||
import { InternalAPI } from "../Netscript/APIWrapper";
|
||||
import { helpers } from "../Netscript/NetscriptHelpers";
|
||||
import {
|
||||
findDevice,
|
||||
inMyrianBounds,
|
||||
removeDevice,
|
||||
getTotalGlitchMult,
|
||||
getNextOSocketRequest,
|
||||
countDevices,
|
||||
resetMyrian,
|
||||
myrian,
|
||||
myrianSize,
|
||||
} from "../Myrian/Myrian";
|
||||
import {
|
||||
installDeviceCost,
|
||||
upgradeEmissionCost,
|
||||
upgradeInstallLvlCost,
|
||||
upgradeMaxEnergyCost,
|
||||
upgradeMoveLvlCost,
|
||||
upgradeReduceLvlCost,
|
||||
upgradeTierCost,
|
||||
upgradeTransferLvlCost,
|
||||
upgradeMaxContentCost,
|
||||
} from "../Myrian/formulas/costs";
|
||||
import { recipes } from "../Myrian/formulas/recipes";
|
||||
import { componentTiers } from "../Myrian/formulas/components";
|
||||
import {
|
||||
frictionMult,
|
||||
glitchMaxLvl,
|
||||
glitchMult,
|
||||
isolationMult,
|
||||
jammingMult,
|
||||
magnetismLoss,
|
||||
virtualizationMult,
|
||||
} from "../Myrian/formulas/glitches";
|
||||
import {
|
||||
adjacent,
|
||||
adjacentCoord2D,
|
||||
contentVulnsValue,
|
||||
inventoryMatches,
|
||||
isDeviceBus,
|
||||
isDeviceContainer,
|
||||
isDeviceISocket,
|
||||
isDeviceOSocket,
|
||||
isDeviceTiered,
|
||||
isEmittingDevice,
|
||||
isEnergyDevice,
|
||||
isInstallingDevice,
|
||||
isMovingDevice,
|
||||
isReducingDevice,
|
||||
isTransferingDevice,
|
||||
pickOne,
|
||||
} from "../Myrian/utils";
|
||||
import { installSpeed, emissionSpeed, moveSpeed, reduceSpeed, transferSpeed } from "../Myrian/formulas/speed";
|
||||
import { NewBattery, NewBus, NewCache, NewISocket, NewLock, NewOSocket, NewReducer } from "../Myrian/NewDevices";
|
||||
import { rustBus } from "../Myrian/glitches/rust";
|
||||
import { Bus, Myrian as IMyrian, Reducer, Battery, ISocket } from "@nsdefs";
|
||||
import { DeviceType, Component, Glitch } from "@enums";
|
||||
|
||||
export function NetscriptMyrian(): InternalAPI<IMyrian> {
|
||||
return {
|
||||
DEUBG_RESET: () => resetMyrian,
|
||||
DEBUG_GIVE_VULNS: (ctx) => (_amount) => {
|
||||
const amount = helpers.number(ctx, "amount", _amount);
|
||||
myrian.vulns += amount;
|
||||
},
|
||||
getDevice: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "id", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return;
|
||||
return JSON.parse(JSON.stringify(device));
|
||||
},
|
||||
getDevices: (__ctx) => () => JSON.parse(JSON.stringify(myrian.devices)),
|
||||
getVulns: () => () => myrian.vulns,
|
||||
renameDevice: (ctx) => (_id, _name) => {
|
||||
const id = helpers.deviceID(ctx, "id", _id);
|
||||
const name = helpers.string(ctx, "name", _name);
|
||||
const device = findDevice(id);
|
||||
if (!device) return false;
|
||||
if (findDevice(name)) return false;
|
||||
device.name = name;
|
||||
return true;
|
||||
},
|
||||
moveBus:
|
||||
(ctx) =>
|
||||
async (_bus, _coord): Promise<boolean> => {
|
||||
const busID = helpers.string(ctx, "bus", _bus);
|
||||
const [x, y] = helpers.coord2d(ctx, "coord", _coord);
|
||||
|
||||
const bus = findDevice(busID, DeviceType.Bus) as Bus;
|
||||
if (!bus) {
|
||||
helpers.log(ctx, () => `bus does not exist`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (!adjacentCoord2D(bus, [x, y])) {
|
||||
helpers.log(ctx, () => `bus ${busID} is not adjacent to [${x}, ${y}]`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
if (!inMyrianBounds(x, y)) {
|
||||
helpers.log(ctx, () => `[${x}, ${y}] is out of bounds`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (findDevice([x, y])) {
|
||||
helpers.log(ctx, () => `[${x}, ${y}] is occupied`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (bus.isBusy) {
|
||||
helpers.log(ctx, () => `bus ${busID} is busy`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const outOfEnergy = bus.energy === 0 ? 10 : 1;
|
||||
|
||||
bus.isBusy = true;
|
||||
return helpers
|
||||
.netscriptDelay(
|
||||
ctx,
|
||||
moveSpeed(bus.moveLvl) * frictionMult(myrian.glitches[Glitch.Friction]) * outOfEnergy,
|
||||
true,
|
||||
)
|
||||
.then(() => {
|
||||
bus.isBusy = false;
|
||||
bus.energy = Math.max(0, bus.energy - magnetismLoss(myrian.glitches[Glitch.Magnetism]));
|
||||
if (findDevice([x, y])) {
|
||||
helpers.log(ctx, () => `[${x}, ${y}] is occupied`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
bus.x = x;
|
||||
bus.y = y;
|
||||
if (myrian.rust[`${x}:${y}`]) rustBus(bus, myrian.glitches[Glitch.Rust]);
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
.finally(() => {
|
||||
bus.isBusy = false;
|
||||
});
|
||||
},
|
||||
formatContent: (ctx) => (_device) => {
|
||||
const deviceID = helpers.deviceID(ctx, "device", _device);
|
||||
|
||||
const device = findDevice(deviceID);
|
||||
if (!device) {
|
||||
helpers.log(ctx, () => `device ${deviceID} not found`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDeviceContainer(device)) {
|
||||
helpers.log(ctx, () => `device ${deviceID} is not a container`);
|
||||
return false;
|
||||
}
|
||||
|
||||
device.content = [];
|
||||
|
||||
if (isDeviceISocket(device)) {
|
||||
const cooldown = emissionSpeed(device.emissionLvl);
|
||||
device.cooldownUntil = Date.now() + cooldown;
|
||||
setTimeout(() => {
|
||||
device.content = new Array(device.maxContent).fill(device.emitting);
|
||||
}, cooldown);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
transfer:
|
||||
(ctx) =>
|
||||
async (_from, _to, _input, _output): Promise<boolean> => {
|
||||
const fromID = helpers.deviceID(ctx, "from", _from);
|
||||
const toID = helpers.deviceID(ctx, "to", _to);
|
||||
const input = _input as Component[];
|
||||
const output = (_output ?? []) as Component[];
|
||||
|
||||
const fromDevice = findDevice(fromID);
|
||||
if (!fromDevice) {
|
||||
helpers.log(ctx, () => `device ${fromID} not found`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (!isDeviceContainer(fromDevice)) {
|
||||
helpers.log(ctx, () => `device ${fromID} is not a container`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const toDevice = findDevice(toID);
|
||||
if (!toDevice) {
|
||||
helpers.log(ctx, () => `device ${toID} not found`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (!isDeviceContainer(toDevice)) {
|
||||
helpers.log(ctx, () => `device ${toID} is not a container`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (!adjacent(fromDevice, toDevice)) {
|
||||
helpers.log(ctx, () => "entities are not adjacent");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (!isDeviceBus(fromDevice) && !isDeviceBus(toDevice)) {
|
||||
helpers.log(ctx, () => "neither device is a bus");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const fromFinalSize = fromDevice.content.length - input.length + output.length;
|
||||
const toFinalSize = toDevice.content.length - output.length + input.length;
|
||||
if (fromFinalSize > fromDevice.maxContent || toFinalSize > toDevice.maxContent) {
|
||||
helpers.log(ctx, () => "not enough space in one of the containers");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
if (fromDevice.isBusy || toDevice.isBusy) {
|
||||
helpers.log(ctx, () => "one of the entities is busy");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const fromContentMap = fromDevice.content.reduce(
|
||||
(acc, c) => ({ ...acc, [c]: (acc[c] ?? 0) + 1 }),
|
||||
{} as Record<Component, number>,
|
||||
);
|
||||
const toContentMap = toDevice.content.reduce(
|
||||
(acc, c) => ({ ...acc, [c]: (acc[c] ?? 0) + 1 }),
|
||||
{} as Record<Component, number>,
|
||||
);
|
||||
|
||||
const inputContentMap = input.reduce(
|
||||
(acc, c) => ({ ...acc, [c]: (acc[c] ?? 0) + 1 }),
|
||||
{} as Record<Component, number>,
|
||||
);
|
||||
const outputContentMap = output.reduce(
|
||||
(acc, c) => ({ ...acc, [c]: (acc[c] ?? 0) + 1 }),
|
||||
{} as Record<Component, number>,
|
||||
);
|
||||
|
||||
const fromHas = (Object.keys(inputContentMap) as Component[]).every(
|
||||
(k) => fromContentMap[k] >= inputContentMap[k],
|
||||
);
|
||||
const toHas = (Object.keys(outputContentMap) as Component[]).every(
|
||||
(k) => toContentMap[k] >= outputContentMap[k],
|
||||
);
|
||||
if (!fromHas || !toHas) {
|
||||
helpers.log(ctx, () => "one of the entities does not have the items");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const bus = [fromDevice, toDevice].find((e) => e.type === DeviceType.Bus) as Bus;
|
||||
const container = [fromDevice, toDevice].find((e) => e.type !== DeviceType.Bus)!;
|
||||
fromDevice.isBusy = true;
|
||||
toDevice.isBusy = true;
|
||||
|
||||
return helpers
|
||||
.netscriptDelay(ctx, transferSpeed(bus.transferLvl) * isolationMult(myrian.glitches[Glitch.Isolation]), true)
|
||||
.then(() => {
|
||||
const previousSize = container.content.length;
|
||||
|
||||
(Object.keys(inputContentMap) as Component[]).forEach((k) => {
|
||||
fromContentMap[k] = (fromContentMap[k] ?? 0) - inputContentMap[k];
|
||||
toContentMap[k] = (toContentMap[k] ?? 0) + inputContentMap[k];
|
||||
});
|
||||
(Object.keys(outputContentMap) as Component[]).forEach((k) => {
|
||||
toContentMap[k] = (toContentMap[k] ?? 0) - outputContentMap[k];
|
||||
fromContentMap[k] = (fromContentMap[k] ?? 0) + outputContentMap[k];
|
||||
});
|
||||
toDevice.content = (Object.keys(toContentMap) as Component[])
|
||||
.map((k) => new Array(toContentMap[k]).fill(k))
|
||||
.flat();
|
||||
|
||||
fromDevice.content = (Object.keys(fromContentMap) as Component[])
|
||||
.map((k) => new Array(fromContentMap[k]).fill(k))
|
||||
.flat();
|
||||
|
||||
if (isDeviceISocket(container) && previousSize > container.content.length) {
|
||||
const cooldown = emissionSpeed(container.emissionLvl);
|
||||
container.cooldownUntil = Date.now() + cooldown;
|
||||
setTimeout(() => {
|
||||
container.content = new Array(container.maxContent).fill(container.emitting);
|
||||
}, cooldown);
|
||||
}
|
||||
if (isDeviceOSocket(container) && inventoryMatches(container.currentRequest, container.content)) {
|
||||
const gain = contentVulnsValue(container.content) * getTotalGlitchMult();
|
||||
myrian.vulns += gain;
|
||||
myrian.totalVulns += gain;
|
||||
container.content = [];
|
||||
const request = getNextOSocketRequest(myrian.glitches[Glitch.Encryption]);
|
||||
container.currentRequest = request;
|
||||
container.maxContent = request.length;
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
.finally(() => {
|
||||
fromDevice.isBusy = false;
|
||||
toDevice.isBusy = false;
|
||||
});
|
||||
},
|
||||
reduce:
|
||||
(ctx) =>
|
||||
async (_busID, _reducerID): Promise<boolean> => {
|
||||
const busID = helpers.deviceID(ctx, "bus", _busID);
|
||||
const reducerID = helpers.deviceID(ctx, "reducer", _reducerID);
|
||||
|
||||
const bus = findDevice(busID, DeviceType.Bus) as Bus;
|
||||
if (!bus) {
|
||||
helpers.log(ctx, () => `bus ${busID} not found`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const reducer = findDevice(reducerID, DeviceType.Reducer) as Reducer;
|
||||
if (!reducer) {
|
||||
helpers.log(ctx, () => `reducer ${reducerID} not found`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (!adjacent(bus, reducer)) {
|
||||
helpers.log(ctx, () => "entites are not adjacent");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const recipe = recipes[reducer.tier].find((r) => inventoryMatches(r.input, reducer.content));
|
||||
|
||||
if (!recipe) {
|
||||
helpers.log(ctx, () => "reducer content matches no recipe");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (bus.isBusy || reducer.isBusy) {
|
||||
helpers.log(ctx, () => "bus or reducer is busy");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
bus.isBusy = true;
|
||||
reducer.isBusy = true;
|
||||
return helpers
|
||||
.netscriptDelay(ctx, reduceSpeed(bus.reduceLvl) * jammingMult(myrian.glitches[Glitch.Jamming]), true)
|
||||
.then(() => {
|
||||
reducer.content = [recipe.output];
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
.finally(() => {
|
||||
bus.isBusy = false;
|
||||
reducer.isBusy = false;
|
||||
});
|
||||
},
|
||||
tweakISocket: (ctx) => async (_bus, _isocket, _component) => {
|
||||
const busID = helpers.deviceID(ctx, "bus", _bus);
|
||||
const isocketID = helpers.deviceID(ctx, "isocket", _isocket);
|
||||
const component = helpers.string(ctx, "component", _component) as Component;
|
||||
|
||||
if (!componentTiers[0].includes(component)) {
|
||||
helpers.log(ctx, () => `component ${component} is not a valid component`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const bus = findDevice(busID, DeviceType.Bus) as Bus;
|
||||
if (!bus) {
|
||||
helpers.log(ctx, () => `bus ${busID} not found`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
const isocket = findDevice(isocketID, DeviceType.ISocket) as ISocket;
|
||||
if (!isocket) {
|
||||
helpers.log(ctx, () => `isocket ${isocketID} not found`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (!adjacent(bus, isocket)) {
|
||||
helpers.log(ctx, () => "bus and isocket are not adjacent");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
bus.isBusy = true;
|
||||
isocket.isBusy = true;
|
||||
|
||||
return helpers
|
||||
.netscriptDelay(
|
||||
ctx,
|
||||
installSpeed(bus.installLvl) * virtualizationMult(myrian.glitches[Glitch.Virtualization]),
|
||||
true,
|
||||
)
|
||||
.then(() => {
|
||||
isocket.emitting = component;
|
||||
isocket.content = [];
|
||||
const cooldown = emissionSpeed(isocket.emissionLvl);
|
||||
isocket.cooldownUntil = Date.now() + cooldown;
|
||||
setTimeout(() => {
|
||||
isocket.content = new Array(isocket.maxContent).fill(isocket.emitting);
|
||||
}, cooldown);
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
.finally(() => {
|
||||
bus.isBusy = false;
|
||||
isocket.isBusy = false;
|
||||
});
|
||||
},
|
||||
energize: (ctx) => async (_bus, _battery) => {
|
||||
const busID = helpers.deviceID(ctx, "bus", _bus);
|
||||
const batteryID = helpers.deviceID(ctx, "battery", _battery);
|
||||
|
||||
const bus = findDevice(busID, DeviceType.Bus) as Bus;
|
||||
if (!bus) {
|
||||
helpers.log(ctx, () => `bus ${busID} not found`);
|
||||
return Promise.resolve(-1);
|
||||
}
|
||||
|
||||
const battery = findDevice(batteryID, DeviceType.Battery) as Battery;
|
||||
if (!battery) {
|
||||
helpers.log(ctx, () => `battery ${batteryID} not found`);
|
||||
return Promise.resolve(-1);
|
||||
}
|
||||
|
||||
const transfer = Math.min(battery.energy, bus.maxEnergy - bus.energy);
|
||||
bus.isBusy = true;
|
||||
battery.isBusy = true;
|
||||
|
||||
return helpers
|
||||
.netscriptDelay(ctx, 100 * transfer, true)
|
||||
.then(() => {
|
||||
bus.energy += transfer;
|
||||
battery.energy -= transfer;
|
||||
return Promise.resolve(transfer);
|
||||
})
|
||||
.finally(() => {
|
||||
bus.isBusy = false;
|
||||
battery.isBusy = false;
|
||||
});
|
||||
},
|
||||
upgradeMaxContent: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "id", _id);
|
||||
const container = findDevice(id);
|
||||
if (!container) {
|
||||
helpers.log(ctx, () => `device ${id} not found`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDeviceContainer(container)) {
|
||||
helpers.log(ctx, () => `device ${id} is not a container`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const cost = upgradeMaxContentCost(container.type, container.maxContent);
|
||||
if (myrian.vulns < cost) {
|
||||
helpers.log(ctx, () => `not enough vulns to upgrade container`);
|
||||
return false;
|
||||
}
|
||||
|
||||
myrian.vulns -= cost;
|
||||
container.maxContent++;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
getUpgradeMaxContentCost: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "id", _id);
|
||||
const container = findDevice(id);
|
||||
if (!container) {
|
||||
helpers.log(ctx, () => `container ${id} not found`);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!isDeviceContainer(container)) {
|
||||
helpers.log(ctx, () => `device ${id} is not a container`);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return upgradeMaxContentCost(container.type, container.maxContent);
|
||||
},
|
||||
|
||||
getDeviceCost: (ctx) => (_type) => {
|
||||
const type = helpers.string(ctx, "type", _type);
|
||||
return installDeviceCost(type as DeviceType, countDevices(type as DeviceType));
|
||||
},
|
||||
|
||||
installDevice: (ctx) => async (_bus, _name, _coord, _deviceType) => {
|
||||
const busID = helpers.deviceID(ctx, "bus", _bus);
|
||||
const name = helpers.string(ctx, "name", _name);
|
||||
const [x, y] = helpers.coord2d(ctx, "coord", _coord);
|
||||
const deviceType = helpers.string(ctx, "deviceType", _deviceType) as DeviceType;
|
||||
|
||||
const bus = findDevice(busID, DeviceType.Bus) as Bus;
|
||||
if (!bus) {
|
||||
helpers.log(ctx, () => `bus ${busID} not found`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (findDevice(name)) {
|
||||
helpers.log(ctx, () => `device ${name} already exists`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const placedDevice = findDevice([x, y]);
|
||||
if (placedDevice) {
|
||||
helpers.log(ctx, () => `location [${x}, ${y}] is occupied`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (bus.isBusy) {
|
||||
helpers.log(ctx, () => `bus ${busID} is busy`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const cost = installDeviceCost(deviceType, countDevices(deviceType));
|
||||
if (myrian.vulns < cost) {
|
||||
helpers.log(ctx, () => `not enough vulns to install device`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
myrian.vulns -= cost;
|
||||
|
||||
if (deviceType === DeviceType.ISocket && y !== 0) {
|
||||
helpers.log(ctx, () => `ISocket must be placed on the top row`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (deviceType === DeviceType.OSocket && y !== myrianSize - 1) {
|
||||
helpers.log(ctx, () => `OSocket must be placed on the bottom row`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
bus.isBusy = true;
|
||||
const lockName = `lock-${busID}`;
|
||||
const lock = NewLock(lockName, x, y);
|
||||
lock.isBusy = true;
|
||||
return helpers
|
||||
.netscriptDelay(
|
||||
ctx,
|
||||
installSpeed(bus.installLvl) * virtualizationMult(myrian.glitches[Glitch.Virtualization]),
|
||||
true,
|
||||
)
|
||||
.then(() => {
|
||||
bus.isBusy = false;
|
||||
removeDevice(lockName);
|
||||
switch (deviceType) {
|
||||
case DeviceType.Bus: {
|
||||
NewBus(name, x, y);
|
||||
break;
|
||||
}
|
||||
case DeviceType.ISocket: {
|
||||
NewISocket(name, x, y, pickOne(componentTiers[0]));
|
||||
break;
|
||||
}
|
||||
case DeviceType.OSocket: {
|
||||
NewOSocket(name, x, y);
|
||||
break;
|
||||
}
|
||||
case DeviceType.Reducer: {
|
||||
NewReducer(name, x, y);
|
||||
break;
|
||||
}
|
||||
case DeviceType.Cache: {
|
||||
NewCache(name, x, y);
|
||||
break;
|
||||
}
|
||||
case DeviceType.Battery: {
|
||||
NewBattery(name, x, y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
.finally(() => {
|
||||
bus.isBusy = false;
|
||||
});
|
||||
},
|
||||
uninstallDevice: (ctx) => async (_bus, _coord) => {
|
||||
const busID = helpers.string(ctx, "bus", _bus);
|
||||
const [x, y] = helpers.coord2d(ctx, "coord", _coord);
|
||||
|
||||
const bus = findDevice(busID, DeviceType.Bus) as Bus;
|
||||
if (!bus) {
|
||||
helpers.log(ctx, () => `bus ${busID} not found`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const placedDevice = findDevice([x, y]);
|
||||
if (!placedDevice) {
|
||||
helpers.log(ctx, () => `location [${x}, ${y}] is empty`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (bus.isBusy || placedDevice.isBusy) {
|
||||
helpers.log(ctx, () => `bus or device is busy`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
bus.isBusy = true;
|
||||
placedDevice.isBusy = true;
|
||||
return helpers
|
||||
.netscriptDelay(
|
||||
ctx,
|
||||
installSpeed(bus.installLvl) * virtualizationMult(myrian.glitches[Glitch.Virtualization]),
|
||||
true,
|
||||
)
|
||||
.then(() => {
|
||||
bus.isBusy = false;
|
||||
placedDevice.isBusy = false;
|
||||
removeDevice([x, y]);
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
.finally(() => {
|
||||
bus.isBusy = false;
|
||||
placedDevice.isBusy = false;
|
||||
});
|
||||
},
|
||||
getUpgradeTierCost: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return -1;
|
||||
if (!isDeviceTiered(device)) return -1;
|
||||
return upgradeTierCost(device.type, device.tier);
|
||||
},
|
||||
upgradeTier: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return false;
|
||||
if (!isDeviceTiered(device)) return false;
|
||||
const cost = upgradeTierCost(device.type, device.tier);
|
||||
if (myrian.vulns < cost) return false;
|
||||
myrian.vulns -= cost;
|
||||
device.tier++;
|
||||
return true;
|
||||
},
|
||||
getUpgradeEmissionLvlCost: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return -1;
|
||||
if (!isEmittingDevice(device)) return -1;
|
||||
return upgradeEmissionCost(device.type, device.emissionLvl);
|
||||
},
|
||||
upgradeEmissionLvl: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return false;
|
||||
if (!isEmittingDevice(device)) return false;
|
||||
const cost = upgradeEmissionCost(device.type, device.emissionLvl);
|
||||
if (myrian.vulns < cost) return false;
|
||||
myrian.vulns -= cost;
|
||||
device.emissionLvl++;
|
||||
return true;
|
||||
},
|
||||
getUpgradeMoveLvlCost: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return -1;
|
||||
if (!isMovingDevice(device)) return -1;
|
||||
return upgradeMoveLvlCost(device.type, device.moveLvl);
|
||||
},
|
||||
upgradeMoveLvl: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return false;
|
||||
if (!isMovingDevice(device)) return false;
|
||||
const cost = upgradeMoveLvlCost(device.type, device.moveLvl);
|
||||
if (myrian.vulns < cost) return false;
|
||||
myrian.vulns -= cost;
|
||||
device.moveLvl++;
|
||||
return true;
|
||||
},
|
||||
getUpgradeTransferLvlCost: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return -1;
|
||||
if (!isTransferingDevice(device)) return -1;
|
||||
return upgradeTransferLvlCost(device.type, device.transferLvl);
|
||||
},
|
||||
upgradeTransferLvl: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return false;
|
||||
if (!isTransferingDevice(device)) return false;
|
||||
const cost = upgradeTransferLvlCost(device.type, device.transferLvl);
|
||||
if (myrian.vulns < cost) return false;
|
||||
myrian.vulns -= cost;
|
||||
device.transferLvl++;
|
||||
return true;
|
||||
},
|
||||
getUpgradeReduceLvlCost: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return -1;
|
||||
if (!isReducingDevice(device)) return -1;
|
||||
return upgradeReduceLvlCost(device.type, device.reduceLvl);
|
||||
},
|
||||
upgradeReduceLvl: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return false;
|
||||
if (!isReducingDevice(device)) return false;
|
||||
const cost = upgradeReduceLvlCost(device.type, device.reduceLvl);
|
||||
if (myrian.vulns < cost) return false;
|
||||
myrian.vulns -= cost;
|
||||
device.reduceLvl++;
|
||||
return true;
|
||||
},
|
||||
getUpgradeInstallLvlCost: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return -1;
|
||||
if (!isInstallingDevice(device)) return -1;
|
||||
return upgradeInstallLvlCost(device.type, device.installLvl);
|
||||
},
|
||||
upgradeInstallLvl: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return false;
|
||||
if (!isInstallingDevice(device)) return false;
|
||||
const cost = upgradeInstallLvlCost(device.type, device.installLvl);
|
||||
if (myrian.vulns < cost) return false;
|
||||
myrian.vulns -= cost;
|
||||
device.installLvl++;
|
||||
return true;
|
||||
},
|
||||
getUpgradeMaxEnergyCost: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return -1;
|
||||
if (!isEnergyDevice(device)) return -1;
|
||||
return upgradeMaxEnergyCost(device.type, device.maxEnergy);
|
||||
},
|
||||
upgradeMaxEnergy: (ctx) => (_id) => {
|
||||
const id = helpers.deviceID(ctx, "device", _id);
|
||||
const device = findDevice(id);
|
||||
if (!device) return false;
|
||||
if (!isEnergyDevice(device)) return false;
|
||||
const cost = upgradeMaxEnergyCost(device.type, device.maxEnergy);
|
||||
if (myrian.vulns < cost) return false;
|
||||
myrian.vulns -= cost;
|
||||
device.maxEnergy++;
|
||||
return true;
|
||||
},
|
||||
setGlitchLvl: (ctx) => async (_glitch, _lvl) => {
|
||||
const glitch = helpers.string(ctx, "glitch", _glitch);
|
||||
const lvl = helpers.number(ctx, "lvl", _lvl);
|
||||
if (lvl < 0 || lvl > glitchMaxLvl[glitch as Glitch]) return Promise.resolve();
|
||||
const currentLvl = myrian.glitches[glitch as Glitch];
|
||||
return helpers.netscriptDelay(ctx, Math.abs(lvl - currentLvl) * 5000, true).then(() => {
|
||||
myrian.glitches[glitch as Glitch] = lvl;
|
||||
});
|
||||
},
|
||||
getGlitchLvl: (ctx) => (_glitch) => {
|
||||
const glitch = helpers.string(ctx, "glitch", _glitch) as Glitch;
|
||||
return myrian.glitches[glitch];
|
||||
},
|
||||
getGlitchMaxLvl: (ctx) => (_glitch) => {
|
||||
const glitch = helpers.string(ctx, "glitch", _glitch) as Glitch;
|
||||
return glitchMaxLvl[glitch];
|
||||
},
|
||||
getGlitchMult: (ctx) => (_glitch) => {
|
||||
const glitch = helpers.string(ctx, "glitch", _glitch) as Glitch;
|
||||
return glitchMult(glitch, myrian.glitches[glitch]);
|
||||
},
|
||||
getTotalGlitchMult: () => () => getTotalGlitchMult(),
|
||||
};
|
||||
}
|
||||
@@ -68,6 +68,7 @@ export class PlayerObject extends Person implements IPlayer {
|
||||
lastUpdate = 0;
|
||||
lastSave = 0;
|
||||
totalPlaytime = 0;
|
||||
myrianConnection = false;
|
||||
|
||||
currentWork: Work | null = null;
|
||||
focus = false;
|
||||
@@ -129,6 +130,7 @@ export class PlayerObject extends Person implements IPlayer {
|
||||
checkForFactionInvitations = generalMethods.checkForFactionInvitations;
|
||||
setBitNodeNumber = generalMethods.setBitNodeNumber;
|
||||
canAccessCotMG = generalMethods.canAccessCotMG;
|
||||
canAccessMyrian = generalMethods.canAccessMyrian;
|
||||
sourceFileLvl = generalMethods.sourceFileLvl;
|
||||
applyEntropy = augmentationMethods.applyEntropy;
|
||||
focusPenalty = generalMethods.focusPenalty;
|
||||
|
||||
@@ -582,6 +582,10 @@ export function canAccessCotMG(this: PlayerObject): boolean {
|
||||
return this.bitNodeN === 13 || this.sourceFileLvl(13) > 0;
|
||||
}
|
||||
|
||||
export function canAccessMyrian(this: PlayerObject): boolean {
|
||||
return this.bitNodeN === 19 || this.sourceFileLvl(19) > 0;
|
||||
}
|
||||
|
||||
export function sourceFileLvl(this: PlayerObject, n: number): number {
|
||||
return this.sourceFiles.get(n) ?? 0;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { initCircadianModulator } from "./Augmentation/Augmentations";
|
||||
import { Go } from "./Go/Go";
|
||||
import { calculateExp } from "./PersonObjects/formulas/skill";
|
||||
import { currentNodeMults } from "./BitNode/BitNodeMultipliers";
|
||||
import { resetMyrian } from "./Myrian/Myrian";
|
||||
|
||||
const BitNode8StartingMoney = 250e6;
|
||||
function delayedDialog(message: string) {
|
||||
@@ -313,11 +314,14 @@ export function prestigeSourceFile(isFlume: boolean): void {
|
||||
updateHashManagerCapacity();
|
||||
}
|
||||
|
||||
if (Player.bitNodeN === 13) {
|
||||
if (Player.bitNodeN === 13 || Player.bitNodeN === 19) {
|
||||
Player.money = CONSTANTS.TravelCost;
|
||||
}
|
||||
staneksGift.prestigeSourceFile();
|
||||
|
||||
Player.myrianConnection = false;
|
||||
resetMyrian();
|
||||
|
||||
// Gain int exp
|
||||
if (Player.sourceFileLvl(5) !== 0 && !isFlume) Player.gainIntelligenceExp(300);
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import { downloadContentAsFile } from "./utils/FileUtils";
|
||||
import { showAPIBreaks } from "./utils/APIBreaks/APIBreak";
|
||||
import { breakInfos261 } from "./utils/APIBreaks/2.6.1";
|
||||
import { handleGetSaveDataError } from "./Netscript/ErrorMessages";
|
||||
import { myrian, loadMyrian } from "./Myrian/Myrian";
|
||||
|
||||
/* SaveObject.js
|
||||
* Defines the object used to save/load games
|
||||
@@ -96,6 +97,7 @@ class BitburnerSaveObject {
|
||||
AllGangsSave = "";
|
||||
LastExportBonus = "0";
|
||||
StaneksGiftSave = "";
|
||||
FactorySave = "";
|
||||
GoSave = "";
|
||||
|
||||
async getSaveData(forceExcludeRunningScripts = false): Promise<SaveData> {
|
||||
@@ -116,6 +118,7 @@ class BitburnerSaveObject {
|
||||
this.VersionSave = JSON.stringify(CONSTANTS.VersionNumber);
|
||||
this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus);
|
||||
this.StaneksGiftSave = JSON.stringify(staneksGift);
|
||||
this.FactorySave = JSON.stringify(myrian);
|
||||
this.GoSave = JSON.stringify(getGoSave());
|
||||
|
||||
if (Player.gang) this.AllGangsSave = JSON.stringify(AllGangs);
|
||||
@@ -761,6 +764,12 @@ async function loadGame(saveData: SaveData): Promise<boolean> {
|
||||
console.warn(`Could not load Staneks Gift from save`);
|
||||
loadStaneksGift("");
|
||||
}
|
||||
|
||||
if (Object.hasOwn(saveObj, "FactorySave")) {
|
||||
loadMyrian(saveObj.FactorySave);
|
||||
} else {
|
||||
console.warn(`Could not load Factory from save`);
|
||||
}
|
||||
if (Object.hasOwn(saveObj, "AliasesSave")) {
|
||||
try {
|
||||
loadAliases(saveObj.AliasesSave);
|
||||
@@ -859,17 +868,17 @@ function createNewUpdateText() {
|
||||
}
|
||||
|
||||
function createBetaUpdateText() {
|
||||
setTimeout(
|
||||
() =>
|
||||
dialogBoxCreate(
|
||||
"You are playing on the beta environment! This branch of the game " +
|
||||
"features the latest developments in the game. This version may be unstable.\n" +
|
||||
"Please report any bugs/issues through the github repository (https://github.com/bitburner-official/bitburner-src/issues) " +
|
||||
"or the Bitburner subreddit (reddit.com/r/bitburner).\n\n" +
|
||||
CONSTANTS.LatestUpdate,
|
||||
),
|
||||
1000,
|
||||
);
|
||||
// setTimeout(
|
||||
// () =>
|
||||
// dialogBoxCreate(
|
||||
// "You are playing on the beta environment! This branch of the game " +
|
||||
// "features the latest developments in the game. This version may be unstable.\n" +
|
||||
// "Please report any bugs/issues through the github repository (https://github.com/bitburner-official/bitburner-src/issues) " +
|
||||
// "or the Bitburner subreddit (reddit.com/r/bitburner).\n\n" +
|
||||
// CONSTANTS.LatestUpdate,
|
||||
// ),
|
||||
// 1000,
|
||||
// );
|
||||
}
|
||||
|
||||
constructorsForReviver.BitburnerSaveObject = BitburnerSaveObject;
|
||||
|
||||
447
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
447
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@@ -5162,6 +5162,447 @@ interface Stanek {
|
||||
acceptGift(): boolean;
|
||||
}
|
||||
|
||||
// declare enum DeviceType {
|
||||
// Bus = "bus",
|
||||
// ISocket = "isocket",
|
||||
// OSocket = "osocket",
|
||||
// Reducer = "reducer",
|
||||
// Cache = "cache",
|
||||
// Lock = "lock",
|
||||
// Battery = "battery",
|
||||
// }
|
||||
|
||||
// declare enum Component {
|
||||
// // tier 0
|
||||
// R0 = "r0",
|
||||
// G0 = "g0",
|
||||
// B0 = "b0",
|
||||
|
||||
// // tier 1
|
||||
// R1 = "r1",
|
||||
// G1 = "g1",
|
||||
// B1 = "b1",
|
||||
|
||||
// Y1 = "y1",
|
||||
// C1 = "c1",
|
||||
// M1 = "m1",
|
||||
|
||||
// // tier 2
|
||||
// R2 = "r2",
|
||||
// G2 = "g2",
|
||||
// B2 = "b2",
|
||||
|
||||
// Y2 = "y2",
|
||||
// C2 = "c2",
|
||||
// M2 = "m2",
|
||||
|
||||
// W2 = "w2",
|
||||
|
||||
// // tier 3
|
||||
// R3 = "r3",
|
||||
// G3 = "g3",
|
||||
// B3 = "b3",
|
||||
|
||||
// Y3 = "y3",
|
||||
// C3 = "c3",
|
||||
// M3 = "m3",
|
||||
|
||||
// W3 = "w3",
|
||||
|
||||
// // tier 4
|
||||
// R4 = "r4",
|
||||
// G4 = "g4",
|
||||
// B4 = "b4",
|
||||
|
||||
// Y4 = "y4",
|
||||
// C4 = "c4",
|
||||
// M4 = "m4",
|
||||
|
||||
// W4 = "w4",
|
||||
|
||||
// // tier 5
|
||||
// R5 = "r5",
|
||||
// G5 = "g5",
|
||||
// B5 = "b5",
|
||||
|
||||
// Y5 = "y5",
|
||||
// C5 = "c5",
|
||||
// M5 = "m5",
|
||||
|
||||
// W5 = "w5",
|
||||
|
||||
// // tier 6
|
||||
// Y6 = "y6",
|
||||
// C6 = "c6",
|
||||
// M6 = "m6",
|
||||
|
||||
// W6 = "w6",
|
||||
|
||||
// // tier 7
|
||||
// W7 = "w7",
|
||||
// }
|
||||
|
||||
// declare enum Glitch {
|
||||
// // Locks spawn at random
|
||||
// Segmentation = "segmentation",
|
||||
// // ISockets and OSockets move around on their own
|
||||
// Roaming = "roaming",
|
||||
// // OSocket ask for more complicated components
|
||||
// Encryption = "encryption",
|
||||
// // Energy starts being consumed (level 0 is no consumption)
|
||||
// Magnetism = "magnetism",
|
||||
// // Hidden tiles on the board, when stepped on the bus loses upgrades
|
||||
// Rust = "rust",
|
||||
// // Move slows down
|
||||
// Friction = "friction",
|
||||
// // Transfer components and charging slows down
|
||||
// Isolation = "isolation",
|
||||
// // Install/Uninstall slows down
|
||||
// Virtualization = "virtualization",
|
||||
// // Reduce slows down
|
||||
// Jamming = "jamming",
|
||||
// }
|
||||
|
||||
export interface BaseDevice {
|
||||
name: string;
|
||||
type: DeviceType;
|
||||
x: number;
|
||||
y: number;
|
||||
isBusy: boolean;
|
||||
}
|
||||
|
||||
export interface Bus extends ContainerDevice, EnergyDevice {
|
||||
type: DeviceType.Bus;
|
||||
moveLvl: number;
|
||||
transferLvl: number;
|
||||
reduceLvl: number;
|
||||
installLvl: number;
|
||||
}
|
||||
|
||||
export interface EnergyDevice extends BaseDevice {
|
||||
energy: number;
|
||||
maxEnergy: number;
|
||||
}
|
||||
|
||||
export interface TieredDevice extends BaseDevice {
|
||||
tier: number;
|
||||
}
|
||||
|
||||
export interface ContainerDevice extends BaseDevice {
|
||||
content: Component[];
|
||||
maxContent: number;
|
||||
}
|
||||
|
||||
export interface ISocket extends ContainerDevice {
|
||||
type: DeviceType.ISocket;
|
||||
emitting: Component;
|
||||
emissionLvl: number;
|
||||
cooldownUntil: number;
|
||||
}
|
||||
|
||||
export interface OSocket extends ContainerDevice {
|
||||
type: DeviceType.OSocket;
|
||||
currentRequest: Component[];
|
||||
}
|
||||
|
||||
export interface Cache extends ContainerDevice {
|
||||
type: DeviceType.Cache;
|
||||
}
|
||||
|
||||
export interface Reducer extends ContainerDevice, TieredDevice {
|
||||
type: DeviceType.Reducer;
|
||||
}
|
||||
|
||||
export interface Lock extends BaseDevice {
|
||||
type: DeviceType.Lock;
|
||||
}
|
||||
|
||||
export interface Battery extends EnergyDevice, TieredDevice {
|
||||
type: DeviceType.Battery;
|
||||
}
|
||||
|
||||
export interface Recipe {
|
||||
input: Component[];
|
||||
output: Component;
|
||||
}
|
||||
|
||||
export type DeviceID = string | [number, number];
|
||||
|
||||
export type Device = Bus | ISocket | OSocket | Reducer | Cache | Lock | Battery;
|
||||
|
||||
interface Myrian {
|
||||
/**
|
||||
* Give yourself some vulns, for testing.
|
||||
* @param n amount of vulns to give
|
||||
*/
|
||||
DEBUG_GIVE_VULNS(n: number): void;
|
||||
/**
|
||||
* Completely reset the myrian os, for debug purposes
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
*/
|
||||
DEUBG_RESET(): void;
|
||||
|
||||
/**
|
||||
* Get device
|
||||
* @remarks
|
||||
* RAM cost: 0GB
|
||||
* @returns device with this ID
|
||||
*/
|
||||
getDevice(device: DeviceID): Device | undefined;
|
||||
|
||||
/**
|
||||
* Get all devices
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns all devices
|
||||
*/
|
||||
getDevices(): Device[];
|
||||
|
||||
/**
|
||||
* get number of vulnerabilities available
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns number of vulnerabilities available
|
||||
*/
|
||||
getVulns(): number;
|
||||
|
||||
/**
|
||||
* Move a bus
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the move succeeded, false otherwise.
|
||||
*/
|
||||
moveBus(bus: DeviceID, coord: [number, number]): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Delete the entire content of a device, typically used for debugging.
|
||||
* @returns true if the formatting succeeded, false otherwise.
|
||||
*/
|
||||
formatContent(device: DeviceID): boolean;
|
||||
|
||||
/**
|
||||
* Transfer components between devices, one of them must be a bus.
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the transfer succeeded, false otherwise.
|
||||
*/
|
||||
transfer(from: DeviceID, to: DeviceID, input: Component[], output?: Component[]): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Make a bus use a reducer in order to produce an component.
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the crafting succeeded, false otherwise.
|
||||
*/
|
||||
reduce(bus: DeviceID, reducer: DeviceID): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Change the component that an isocket emits.
|
||||
* @param component tier 0 component that the isocket should emit
|
||||
* @returns true if the tweak succeeded, false otherwise.
|
||||
*/
|
||||
tweakISocket(bus: DeviceID, isocket: DeviceID, component: Component): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Charge a bus with a battery, restoring it's energy.
|
||||
* @returns positive number for the amount of energy transfered, -1 on failure.
|
||||
*/
|
||||
energize(bus: DeviceID, battery: DeviceID): Promise<number>;
|
||||
|
||||
/**
|
||||
* Get the cost of a device.
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns cost of the next device of that type
|
||||
*/
|
||||
getDeviceCost(type: DeviceType): number;
|
||||
|
||||
/**
|
||||
* Make a bus install a new device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the installation succeeded, false otherwise.
|
||||
*/
|
||||
installDevice(bus: DeviceID, name: string, coord: [number, number], deviceType: DeviceType): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Make a bus uninstall a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the uninstallation succeeded, false otherwise.
|
||||
*/
|
||||
uninstallDevice(bus: DeviceID, coord: [number, number]): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Rename a device, no 2 entity can have the same name
|
||||
* @returns true if the rename succeeded, false otherwise.
|
||||
*/
|
||||
renameDevice(device: DeviceID, name: string): boolean;
|
||||
|
||||
/**
|
||||
* Upgrade the max content of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the upgrade succeeded, false otherwise.
|
||||
*/
|
||||
upgradeMaxContent(device: DeviceID): boolean;
|
||||
|
||||
/**
|
||||
* Get the cost of upgrading the content of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns cost of upgrading the content of a device, -1 on failure.
|
||||
*/
|
||||
getUpgradeMaxContentCost(device: DeviceID): number;
|
||||
|
||||
/**
|
||||
* Upgrade the tier of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the upgrade succeeded, false otherwise.
|
||||
*/
|
||||
upgradeTier(device: DeviceID): boolean;
|
||||
|
||||
/**
|
||||
* Get the cost of upgrading the tier of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns cost of upgrading the tier of a device, -1 on failure.
|
||||
*/
|
||||
getUpgradeTierCost(device: DeviceID): number;
|
||||
|
||||
/**
|
||||
* Get the cost of upgrading the emission of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns cost of upgrading the emission of a device, -1 on failure.
|
||||
*/
|
||||
getUpgradeEmissionLvlCost(device: DeviceID): number;
|
||||
|
||||
/**
|
||||
* Upgrade the emissionLvl of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the upgrade succeeded, false otherwise.
|
||||
*/
|
||||
upgradeEmissionLvl(device: DeviceID): boolean;
|
||||
|
||||
/**
|
||||
* Get the cost of upgrading the moveLvl of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns cost of upgrading the moveLvl of a device, -1 on failure.
|
||||
*/
|
||||
getUpgradeMoveLvlCost(device: DeviceID): number;
|
||||
|
||||
/**
|
||||
* Upgrade the moveLvl of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the upgrade succeeded, false otherwise.
|
||||
*/
|
||||
upgradeMoveLvl(device: DeviceID): boolean;
|
||||
|
||||
/**
|
||||
* Get the cost of upgrading the transferLvl of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns cost of upgrading the transferLvl of a device, -1 on failure.
|
||||
*/
|
||||
getUpgradeTransferLvlCost(device: DeviceID): number;
|
||||
|
||||
/**
|
||||
* Upgrade the moveLvl of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the upgrade succeeded, false otherwise.
|
||||
*/
|
||||
upgradeTransferLvl(device: DeviceID): boolean;
|
||||
|
||||
/**
|
||||
* Get the cost of upgrading the reduceLvl of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns cost of upgrading the reduceLvl of a device, -1 on failure.
|
||||
*/
|
||||
getUpgradeReduceLvlCost(device: DeviceID): number;
|
||||
|
||||
/**
|
||||
* Upgrade the reduceLvl of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the upgrade succeeded, false otherwise.
|
||||
*/
|
||||
upgradeReduceLvl(device: DeviceID): boolean;
|
||||
|
||||
/**
|
||||
* Get the cost of upgrading the installLvl of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns cost of upgrading the installLvl of a device, -1 on failure.
|
||||
*/
|
||||
getUpgradeInstallLvlCost(device: DeviceID): number;
|
||||
|
||||
/**
|
||||
* Upgrade the installLvl of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the upgrade succeeded, false otherwise.
|
||||
*/
|
||||
upgradeInstallLvl(device: DeviceID): boolean;
|
||||
|
||||
/**
|
||||
* Get the cost of upgrading the maxEnergy of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns cost of upgrading the maxEnergy of a device, -1 on failure.
|
||||
*/
|
||||
getUpgradeMaxEnergyCost(device: DeviceID): number;
|
||||
|
||||
/**
|
||||
* Upgrade the maxEnergy of a device
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
* @returns true if the upgrade succeeded, false otherwise.
|
||||
*/
|
||||
upgradeMaxEnergy(device: DeviceID): boolean;
|
||||
|
||||
/**
|
||||
* Set the lvl of a glitch
|
||||
* @param glitch name of the glitch
|
||||
* @param lvl new lvl of the glitch
|
||||
*/
|
||||
setGlitchLvl(glitch: Glitch, lvl: number): Promise<void>;
|
||||
|
||||
/**
|
||||
* Get the lvl of a glitch
|
||||
* @param glitch name of the glitch
|
||||
* @returns current lvl of the glitch
|
||||
*/
|
||||
getGlitchLvl(glitch: Glitch): number;
|
||||
|
||||
/**
|
||||
* Get the max lvl of a glitch
|
||||
* @param glitch name of the glitch
|
||||
* @returns max lvl of the glitch
|
||||
*/
|
||||
getGlitchMaxLvl(glitch: Glitch): number;
|
||||
|
||||
/**
|
||||
* Get the vulns multiplier for a glitch
|
||||
* @param glitch name of the glitch
|
||||
* @returns multiplier for the glitch
|
||||
*/
|
||||
getGlitchMult(glitch: Glitch): number;
|
||||
|
||||
/**
|
||||
* Get the total vulns multiplier for all glitches
|
||||
* @returns total vulns multiplier
|
||||
*/
|
||||
getTotalGlitchMult(): number;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
interface InfiltrationReward {
|
||||
tradeRep: number;
|
||||
@@ -5367,6 +5808,12 @@ export interface NS {
|
||||
*/
|
||||
readonly stanek: Stanek;
|
||||
|
||||
/**
|
||||
* Namespace for myrian functions. Contains spoilers.
|
||||
* @remarks RAM cost: 0 GB
|
||||
*/
|
||||
readonly myrian: Myrian;
|
||||
|
||||
/**
|
||||
* Namespace for infiltration functions.
|
||||
* @remarks RAM cost: 0 GB
|
||||
|
||||
@@ -34,6 +34,7 @@ import CheckIcon from "@mui/icons-material/Check"; // Milestones
|
||||
import HelpIcon from "@mui/icons-material/Help"; // Tutorial
|
||||
import SettingsIcon from "@mui/icons-material/Settings"; // options
|
||||
import DeveloperBoardIcon from "@mui/icons-material/DeveloperBoard"; // Dev
|
||||
import SettingsInputComponentIcon from "@mui/icons-material/SettingsInputComponent"; // Myrian
|
||||
import EmojiEventsIcon from "@mui/icons-material/EmojiEvents"; // Achievements
|
||||
import AccountBoxIcon from "@mui/icons-material/AccountBox";
|
||||
import PublicIcon from "@mui/icons-material/Public";
|
||||
@@ -351,6 +352,7 @@ export function SidebarRoot(props: { page: Page }): React.ReactElement {
|
||||
canCorporation && { key_: Page.Corporation, icon: BusinessIcon },
|
||||
canGang && { key_: Page.Gang, icon: SportsMmaIcon },
|
||||
canIPvGO && { key_: Page.Go, icon: BorderInnerSharp },
|
||||
Player.myrianConnection && { key_: Page.MyrianOS, icon: SettingsInputComponentIcon },
|
||||
]}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
@@ -256,4 +256,20 @@ export function initSourceFiles() {
|
||||
</>
|
||||
),
|
||||
);
|
||||
|
||||
SourceFiles.SourceFile19 = new SourceFile(
|
||||
19,
|
||||
(
|
||||
<>
|
||||
This Source-File grants the following benefits:
|
||||
<br />
|
||||
<br />
|
||||
Level 1: Reduce the effect of the Rust Glitch
|
||||
<br />
|
||||
Level 2: Reduce the effect of the Magnetism Glitch
|
||||
<br />
|
||||
Level 3: Reduce the effect of the Friction, Isolation, Virtualization, and Jamming Glitch
|
||||
</>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -360,31 +360,33 @@ const Engine: {
|
||||
|
||||
Player.lastUpdate = Engine._lastUpdate;
|
||||
Engine.start(); // Run main game loop and Scripts loop
|
||||
const timeOfflineString = convertTimeMsToTimeElapsedString(time);
|
||||
setTimeout(
|
||||
() =>
|
||||
AlertEvents.emit(
|
||||
<>
|
||||
<Typography>Offline for {timeOfflineString}. While you were offline:</Typography>
|
||||
<ul>
|
||||
<li>
|
||||
<Typography>
|
||||
Your scripts generated <Money money={offlineHackingIncome} />
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography>Your Hacknet Nodes generated {hacknetProdInfo}</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography>
|
||||
You gained <Reputation reputation={offlineReputation} /> reputation divided amongst your factions
|
||||
</Typography>
|
||||
</li>
|
||||
</ul>
|
||||
</>,
|
||||
),
|
||||
250,
|
||||
);
|
||||
if (process.env.NODE_ENV !== "development") {
|
||||
const timeOfflineString = convertTimeMsToTimeElapsedString(time);
|
||||
setTimeout(
|
||||
() =>
|
||||
AlertEvents.emit(
|
||||
<>
|
||||
<Typography>Offline for {timeOfflineString}. While you were offline:</Typography>
|
||||
<ul>
|
||||
<li>
|
||||
<Typography>
|
||||
Your scripts generated <Money money={offlineHackingIncome} />
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography>Your Hacknet Nodes generated {hacknetProdInfo}</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography>
|
||||
You gained <Reputation reputation={offlineReputation} /> reputation divided amongst your factions
|
||||
</Typography>
|
||||
</li>
|
||||
</ul>
|
||||
</>,
|
||||
),
|
||||
250,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// No save found, start new game
|
||||
FormatsNeedToChange.emit();
|
||||
|
||||
@@ -73,6 +73,7 @@ import { useRerender } from "./React/hooks";
|
||||
import { HistoryProvider } from "./React/Documentation";
|
||||
import { GoRoot } from "../Go/ui/GoRoot";
|
||||
import { isBitNodeFinished } from "../BitNode/BitNodeUtils";
|
||||
import { MyrianRoot } from "../Myrian/ui/MyrianRoot";
|
||||
|
||||
const htmlLocation = location;
|
||||
|
||||
@@ -120,6 +121,7 @@ function determineStartPage(): PageWithContext {
|
||||
if (Player.currentWork !== null) {
|
||||
return { page: Page.Work };
|
||||
}
|
||||
return { page: Page.MyrianOS };
|
||||
return { page: Page.Terminal };
|
||||
}
|
||||
|
||||
@@ -365,6 +367,10 @@ export function GameRoot(): React.ReactElement {
|
||||
mainPage = <GoRoot />;
|
||||
break;
|
||||
}
|
||||
case Page.MyrianOS: {
|
||||
mainPage = <MyrianRoot />;
|
||||
break;
|
||||
}
|
||||
case Page.Achievements: {
|
||||
mainPage = <AchievementsRoot />;
|
||||
break;
|
||||
|
||||
@@ -37,6 +37,7 @@ export enum SimplePage {
|
||||
Recovery = "Recovery",
|
||||
Achievements = "Achievements",
|
||||
ThemeBrowser = "Theme Browser",
|
||||
MyrianOS = "Myrian OS",
|
||||
}
|
||||
|
||||
export enum ComplexPage {
|
||||
|
||||
Reference in New Issue
Block a user