mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-05-23 08:02:04 +02:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da4c7a01dd | |||
| 0e39a5f603 | |||
| b8dc41faca | |||
| cd94288da0 | |||
| aaf249df11 | |||
| 827f881be4 | |||
| 6095f2e1ac | |||
| e0d8a3e201 | |||
| 016ffbf8c7 | |||
| 3ebe82c60a | |||
| 64b25469ea | |||
| d060b6b1f0 | |||
| f34116c2b1 |
Generated
+30
-28
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "bitburner",
|
"name": "bitburner",
|
||||||
"version": "3.0.1",
|
"version": "3.0.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bitburner",
|
"name": "bitburner",
|
||||||
"version": "3.0.1",
|
"version": "3.0.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@catloversg/steamworks.js": "0.0.3",
|
"@catloversg/steamworks.js": "0.0.3",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
"electron-log": "^4.4.8",
|
"electron-log": "^4.4.8",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"lodash": "^4.17.21"
|
"lodash": "^4.18.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@catloversg/steamworks.js": {
|
"node_modules/@catloversg/steamworks.js": {
|
||||||
@@ -37,14 +37,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "8.12.0",
|
"version": "8.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
|
||||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fast-uri": "^3.0.1",
|
||||||
"json-schema-traverse": "^1.0.0",
|
"json-schema-traverse": "^1.0.0",
|
||||||
"require-from-string": "^2.0.2",
|
"require-from-string": "^2.0.2"
|
||||||
"uri-js": "^4.2.2"
|
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -162,6 +163,22 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-uri": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fastify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fastify"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/find-up": {
|
"node_modules/find-up": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||||
@@ -204,9 +221,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
@@ -301,14 +319,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/punycode": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
|
|
||||||
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/require-from-string": {
|
"node_modules/require-from-string": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
@@ -348,14 +358,6 @@
|
|||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/uri-js": {
|
|
||||||
"version": "4.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
|
||||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
|
||||||
"dependencies": {
|
|
||||||
"punycode": "^2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bitburner",
|
"name": "bitburner",
|
||||||
"version": "3.0.1",
|
"version": "3.0.2",
|
||||||
"description": "A cyberpunk-themed programming incremental game",
|
"description": "A cyberpunk-themed programming incremental game",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"author": "Daniel Xie, hydroflame, et al.",
|
"author": "Daniel Xie, hydroflame, et al.",
|
||||||
@@ -28,6 +28,6 @@
|
|||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
"electron-log": "^4.4.8",
|
"electron-log": "^4.4.8",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"lodash": "^4.17.21"
|
"lodash": "^4.18.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Use esm for top-level await -->
|
<!-- Use esm for top-level await -->
|
||||||
<script type="module">
|
<script type="module">
|
||||||
|
if (!window.indexedDB || typeof window.indexedDB.databases !== "function") {
|
||||||
|
const errorMessage = `Your browser is too outdated. Please update your browser.\n\nUserAgent: ${navigator.userAgent}`;
|
||||||
|
alert(errorMessage);
|
||||||
|
// This is the simplest way to stop execution in top-level code without using a labeled block or IIFE.
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
const databaseName = "bitburnerSave";
|
const databaseName = "bitburnerSave";
|
||||||
// Check src/db.ts to see why the current max version is 2. If the database version is greater than this value, it
|
// Check src/db.ts to see why the current max version is 2. If the database version is greater than this value, it
|
||||||
// means that the code in this file is outdated.
|
// means that the code in this file is outdated.
|
||||||
@@ -35,7 +41,6 @@
|
|||||||
const database = databases.find((info) => info.name === databaseName);
|
const database = databases.find((info) => info.name === databaseName);
|
||||||
if (!database) {
|
if (!database) {
|
||||||
alert("There is no save data");
|
alert("There is no save data");
|
||||||
// This is the simplest way to stop execution in top-level code without using a labeled block or IIFE.
|
|
||||||
throw new Error("There is no save data");
|
throw new Error("There is no save data");
|
||||||
}
|
}
|
||||||
if (database.version === undefined || database.version > maxDatabaseVersion) {
|
if (database.version === undefined || database.version > maxDatabaseVersion) {
|
||||||
@@ -134,7 +134,7 @@ Calculate hack time.
|
|||||||
|
|
||||||
</td><td>
|
</td><td>
|
||||||
|
|
||||||
Calculate the security decrease from a weaken operation. Unlike other hacking formulas, weaken effect depends only on thread count and core count, not on server or player properties. The core bonus formula is `1 + (cores - 1) / 16}`<!-- -->.
|
Calculate the security decrease from a weaken operation. Unlike other hacking formulas, weaken effect depends only on thread count and core count, not on server or player properties. The core bonus formula is `1 + (cores - 1) / 16`<!-- -->.
|
||||||
|
|
||||||
|
|
||||||
</td></tr>
|
</td></tr>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
## HackingFormulas.weakenEffect() method
|
## HackingFormulas.weakenEffect() method
|
||||||
|
|
||||||
Calculate the security decrease from a weaken operation. Unlike other hacking formulas, weaken effect depends only on thread count and core count, not on server or player properties. The core bonus formula is `1 + (cores - 1) / 16}`<!-- -->.
|
Calculate the security decrease from a weaken operation. Unlike other hacking formulas, weaken effect depends only on thread count and core count, not on server or player properties. The core bonus formula is `1 + (cores - 1) / 16`<!-- -->.
|
||||||
|
|
||||||
**Signature:**
|
**Signature:**
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Terminate current script and start another in a defined number of milliseconds.
|
|||||||
**Signature:**
|
**Signature:**
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
spawn(script: string, threadOrOptions?: number | SpawnOptions, ...args: ScriptArg[]): void;
|
spawn(script: string, threadOrOptions?: number | SpawnOptions, ...args: ScriptArg[]): never;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
@@ -82,7 +82,7 @@ Additional arguments to pass into the new script that is being run.
|
|||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
void
|
never
|
||||||
|
|
||||||
## Remarks
|
## Remarks
|
||||||
|
|
||||||
|
|||||||
Generated
+724
-362
File diff suppressed because it is too large
Load Diff
+12
-12
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "bitburner",
|
"name": "bitburner",
|
||||||
"license": "SEE LICENSE IN license.txt",
|
"license": "SEE LICENSE IN license.txt",
|
||||||
"version": "3.0.1",
|
"version": "3.0.2",
|
||||||
"main": "electron-main.js",
|
"main": "electron-main.js",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Daniel Xie, hydroflame, et al."
|
"name": "Daniel Xie, hydroflame, et al."
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"@mui/material": "^5.18.0",
|
"@mui/material": "^5.18.0",
|
||||||
"@mui/styles": "^5.18.0",
|
"@mui/styles": "^5.18.0",
|
||||||
"@mui/system": "^5.18.0",
|
"@mui/system": "^5.18.0",
|
||||||
"@swc/wasm-web": "^1.9.3",
|
"@swc/wasm-web": "^1.15.33",
|
||||||
"@types/estree": "^1.0.2",
|
"@types/estree": "^1.0.2",
|
||||||
"@types/react-syntax-highlighter": "^15.5.13",
|
"@types/react-syntax-highlighter": "^15.5.13",
|
||||||
"acorn": "^8.11.3",
|
"acorn": "^8.11.3",
|
||||||
@@ -56,20 +56,20 @@
|
|||||||
"description": "A cyberpunk-themed incremental game",
|
"description": "A cyberpunk-themed incremental game",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.29.0",
|
"@babel/core": "^7.29.0",
|
||||||
"@babel/preset-env": "^7.29.2",
|
"@babel/preset-env": "^7.29.5",
|
||||||
"@babel/preset-react": "^7.28.5",
|
"@babel/preset-react": "^7.28.5",
|
||||||
"@babel/preset-typescript": "^7.28.5",
|
"@babel/preset-typescript": "^7.28.5",
|
||||||
"@electron/packager": "^18.4.4",
|
"@electron/packager": "^18.4.4",
|
||||||
"@mathjax/src": "^4.1.0",
|
"@mathjax/src": "^4.1.2",
|
||||||
"@microsoft/api-documenter": "^7.26.34",
|
"@microsoft/api-documenter": "^7.26.34",
|
||||||
"@microsoft/api-extractor": "^7.52.13",
|
"@microsoft/api-extractor": "^7.58.7",
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.1",
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.1",
|
||||||
"@swc/core": "^1.12.14",
|
"@swc/core": "^1.15.33",
|
||||||
"@types/babel__standalone": "^7.1.9",
|
"@types/babel__standalone": "^7.1.9",
|
||||||
"@types/bcryptjs": "^2.4.4",
|
"@types/bcryptjs": "^2.4.4",
|
||||||
"@types/convert-source-map": "^2.0.3",
|
"@types/convert-source-map": "^2.0.3",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/lodash": "^4.17.20",
|
"@types/lodash": "^4.17.24",
|
||||||
"@types/react": "^17.0.89",
|
"@types/react": "^17.0.89",
|
||||||
"@types/react-beautiful-dnd": "^13.1.8",
|
"@types/react-beautiful-dnd": "^13.1.8",
|
||||||
"@types/react-dom": "^17.0.26",
|
"@types/react-dom": "^17.0.26",
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.18.1",
|
||||||
"monaco-editor": "^0.55.1",
|
"monaco-editor": "^0.55.1",
|
||||||
"monaco-editor-webpack-plugin": "^7.1.1",
|
"monaco-editor-webpack-plugin": "^7.1.1",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
@@ -99,10 +99,10 @@
|
|||||||
"style-loader": "^4.0.0",
|
"style-loader": "^4.0.0",
|
||||||
"tinybench": "^6.0.0",
|
"tinybench": "^6.0.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"webpack": "^5.105.4",
|
"webpack": "^5.106.2",
|
||||||
"webpack-bundle-analyzer": "^5.2.0",
|
"webpack-bundle-analyzer": "^5.3.0",
|
||||||
"webpack-cli": "^6.0.1",
|
"webpack-cli": "^7.0.2",
|
||||||
"webpack-dev-server": "^5.2.2",
|
"webpack-dev-server": "^5.2.4",
|
||||||
"xml-formatter": "^3.6.7"
|
"xml-formatter": "^3.6.7"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|||||||
+4
-51
@@ -4,8 +4,8 @@
|
|||||||
* Constants for specific mechanics or features will NOT be here.
|
* Constants for specific mechanics or features will NOT be here.
|
||||||
*/
|
*/
|
||||||
export const CONSTANTS = {
|
export const CONSTANTS = {
|
||||||
VersionString: "3.0.1",
|
VersionString: "3.0.2",
|
||||||
isDevBranch: false,
|
isDevBranch: true,
|
||||||
isInTestEnvironment: globalThis.process?.env?.JEST_WORKER_ID !== undefined,
|
isInTestEnvironment: globalThis.process?.env?.JEST_WORKER_ID !== undefined,
|
||||||
VersionNumber: 51,
|
VersionNumber: 51,
|
||||||
|
|
||||||
@@ -111,57 +111,10 @@ export const CONSTANTS = {
|
|||||||
|
|
||||||
// Also update Documentation/doc/en/changelog.md when appropriate (when doing a release)
|
// Also update Documentation/doc/en/changelog.md when appropriate (when doing a release)
|
||||||
LatestUpdate: `
|
LatestUpdate: `
|
||||||
## v3.0.1: 17 May 2026
|
## v3.0.2: Dev version last updated 17 May 2026
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
- Change getServer return type; rename getServerAuthDetails and add missing dnet properties (#2746) (@ficocelliguy)
|
|
||||||
|
|
||||||
### MISC
|
### MISC
|
||||||
|
|
||||||
- Cache reward fixes (#2731) (@ficocelliguy)
|
- No changes yet
|
||||||
- Fix typo in darknet authentication response message (#2734) (@catloversg)
|
|
||||||
- Fix: Tutorial links to outdated faq url (#2733) (@catloversg)
|
|
||||||
- Fix: Player can switch tabs without losing focus on current work (#2724) (@catloversg)
|
|
||||||
- Add new command to upload a directory (#2659) (@hexagonrecursion)
|
|
||||||
- Add HJKL key mappings for infiltration arrows (#2742) (@mahlquistj)
|
|
||||||
- Prevent generating malformed darknet server hostname (#2744) (@catloversg)
|
|
||||||
- Support angle bracket type assertions in RAM calculation (#2751) (@catloversg)
|
|
||||||
- Fix typo in augmentation description (#2760) (@gmcew)
|
|
||||||
- Reduce the RAM cost of ns.rm() to match ns.scp() (#2761) (@NagaOuroboros)
|
|
||||||
- Temporarily remove darknet servers with unusual hostnames (#2757) (@catloversg)
|
|
||||||
- Fix: Duplicate .lit and .cache files can be generated in dnet (#2763) (@catloversg)
|
|
||||||
- Allow getFunctionRamCost to get base RAM cost for scripts (#2771) (@Mathekatze)
|
|
||||||
|
|
||||||
### DOCUMENTATION
|
|
||||||
|
|
||||||
- Remove TS type annotation from doc example script (#2721) (@ficocelliguy)
|
|
||||||
- Update list of RFA community tools (#2722) (@CTNOriginals)
|
|
||||||
- Fix incorrect cloud API example (#2738) (@catloversg)
|
|
||||||
- Remove non-existent influence namespace in Darknet documentation (#2748) (@Berdes)
|
|
||||||
- Remove spoiler for Offline scripts and bonus time page and make it accessible early-game (#2749) (@Berdes)
|
|
||||||
- Clarify ns.scp and ns.isRunning (#2769) (@catloversg)
|
|
||||||
|
|
||||||
### SPOILER CHANGES - UI
|
|
||||||
|
|
||||||
- Show hints of Gang mechanic in pre-endgame (#2723) (@catloversg)
|
|
||||||
- Break out Darknet BN and player mults as separate entries (#2745) (@gmcew)
|
|
||||||
|
|
||||||
### SPOILER CHANGES - MISC
|
|
||||||
|
|
||||||
- Cancel faction work instead of finishing it when creating gang (#2726) (@catloversg)
|
|
||||||
- Lower darknet BN money mults on hard nodes (#2743) (@ficocelliguy)
|
|
||||||
- Change getDarkwebPrograms return type to ProgramName[] (#2754) (@catloversg)
|
|
||||||
- Fix: getAugmentationBasePrice ignores bitnode mult for SoA augs (#2756) (@SAY-5)
|
|
||||||
- Rebalance hash base cost of generating coding contract (#2759) (@gmcew)
|
|
||||||
- Ensure induceServerMigration moves servers randomly (#2767) (@catloversg)
|
|
||||||
- Do not decrease player reputation when failing bladeburner actions (#2768) (@catloversg)
|
|
||||||
|
|
||||||
### CODEBASE/REFACTOR/WORKFLOW/JEST/TOOL/DEPS
|
|
||||||
|
|
||||||
- Update action versions (#2718) (@Snarling)
|
|
||||||
- Remove duplicate getStockFromSymbol function (#2725) (@catloversg)
|
|
||||||
- Update game version (#2732) (@catloversg)
|
|
||||||
- Harden saving to avoid save data corruption (#2755) (@catloversg)
|
|
||||||
`,
|
`,
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { resolveCacheFilePath } from "../../Paths/CacheFilePath";
|
|||||||
import type { CacheResult } from "@nsdefs";
|
import type { CacheResult } from "@nsdefs";
|
||||||
import { addClue, cctCooldownReached } from "./effects";
|
import { addClue, cctCooldownReached } from "./effects";
|
||||||
import { getBitNodeMultipliers } from "../../BitNode/BitNode";
|
import { getBitNodeMultipliers } from "../../BitNode/BitNode";
|
||||||
|
import { pluralize } from "../../utils/I18nUtils";
|
||||||
|
|
||||||
export const generateCacheFilename = (isPhishingCache: boolean, prefix?: string) => {
|
export const generateCacheFilename = (isPhishingCache: boolean, prefix?: string) => {
|
||||||
const filenamePrefix = prefix ?? cachePrefixes[Math.floor(Math.random() * cachePrefixes.length)];
|
const filenamePrefix = prefix ?? cachePrefixes[Math.floor(Math.random() * cachePrefixes.length)];
|
||||||
@@ -117,14 +118,11 @@ export const getStockReward = (difficulty: number): string => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getDataFileReward = (difficulty: number, server: DarknetServer): string => {
|
export const getDataFileReward = (difficulty: number, server: DarknetServer): string => {
|
||||||
const currentDataFiles = server.textFiles.size;
|
const dataFiles = [...addClue(server), ...addClue(server)];
|
||||||
addClue(server);
|
if (dataFiles.length === 0) {
|
||||||
addClue(server);
|
|
||||||
const dataFilesGained = server.textFiles.size - currentDataFiles;
|
|
||||||
if (dataFilesGained === 0) {
|
|
||||||
return getMoneyReward(difficulty);
|
return getMoneyReward(difficulty);
|
||||||
}
|
}
|
||||||
return `You have discovered a data file cache!`;
|
return `You have discovered ${pluralize(dataFiles.length, "data file cache")}: ${dataFiles.join(", ")}.`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getProgramAndStockMarketRelatedRewards = (difficulty: number): string => {
|
export const getProgramAndStockMarketRelatedRewards = (difficulty: number): string => {
|
||||||
|
|||||||
@@ -121,12 +121,14 @@ export const calculatePasswordAttemptChaGain = (server: DarknetServerData, threa
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: balance password clue spawn rate
|
// TODO: balance password clue spawn rate
|
||||||
export const addClue = (server: DarknetServer) => {
|
export const addClue = (server: DarknetServer): string[] => {
|
||||||
|
const files = [];
|
||||||
// Basic mechanics hints
|
// Basic mechanics hints
|
||||||
if ((Math.random() < 0.7 && server.difficulty <= 3) || Math.random() < 0.1) {
|
if ((Math.random() < 0.7 && server.difficulty <= 3) || Math.random() < 0.1) {
|
||||||
const hint: LiteratureName = hintLiterature[Math.floor(Math.random() * hintLiterature.length)];
|
const hint: LiteratureName = hintLiterature[Math.floor(Math.random() * hintLiterature.length)];
|
||||||
if (hint && !server.messages.includes(hint)) {
|
if (hint && !server.messages.includes(hint)) {
|
||||||
server.messages.push(hint);
|
server.messages.push(hint);
|
||||||
|
files.push(hint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +139,8 @@ export const addClue = (server: DarknetServer) => {
|
|||||||
const start = Math.floor(Math.random() * (commonPasswordDictionary.length - length));
|
const start = Math.floor(Math.random() * (commonPasswordDictionary.length - length));
|
||||||
const commonPasswords = commonPasswordDictionary.slice(start, start + length).join(", ");
|
const commonPasswords = commonPasswordDictionary.slice(start, start + length).join(", ");
|
||||||
server.writeToTextFile(hintFileName, `Some common passwords include ${commonPasswords}`);
|
server.writeToTextFile(hintFileName, `Some common passwords include ${commonPasswords}`);
|
||||||
return;
|
files.push(hintFileName);
|
||||||
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
// connected neighboring server's password (does not include server name)
|
// connected neighboring server's password (does not include server name)
|
||||||
@@ -150,7 +153,8 @@ export const addClue = (server: DarknetServer) => {
|
|||||||
const neighboringServer = neighboringServerName ? getDarknetServer(neighboringServerName) : null;
|
const neighboringServer = neighboringServerName ? getDarknetServer(neighboringServerName) : null;
|
||||||
if (neighboringServer) {
|
if (neighboringServer) {
|
||||||
server.writeToTextFile(passwordHintName, `Remember this password: ${neighboringServer.password}`);
|
server.writeToTextFile(passwordHintName, `Remember this password: ${neighboringServer.password}`);
|
||||||
return;
|
files.push(passwordHintName);
|
||||||
|
return files;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +165,8 @@ export const addClue = (server: DarknetServer) => {
|
|||||||
if (targetServer) {
|
if (targetServer) {
|
||||||
const contents = `Server: ${targetServer.hostname} Password: "${targetServer.password}"`;
|
const contents = `Server: ${targetServer.hostname} Password: "${targetServer.password}"`;
|
||||||
server.writeToTextFile(hintFileName, contents);
|
server.writeToTextFile(hintFileName, contents);
|
||||||
return;
|
files.push(hintFileName);
|
||||||
|
return files;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +174,8 @@ export const addClue = (server: DarknetServer) => {
|
|||||||
const hintFileName = getClueFileName(notebookFileNames);
|
const hintFileName = getClueFileName(notebookFileNames);
|
||||||
const loreNote = packetSniffPhrases[Math.floor(Math.random() * packetSniffPhrases.length)];
|
const loreNote = packetSniffPhrases[Math.floor(Math.random() * packetSniffPhrases.length)];
|
||||||
server.writeToTextFile(hintFileName, loreNote);
|
server.writeToTextFile(hintFileName, loreNote);
|
||||||
return;
|
files.push(hintFileName);
|
||||||
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Math.random() < 0.7) {
|
if (Math.random() < 0.7) {
|
||||||
@@ -179,9 +185,11 @@ export const addClue = (server: DarknetServer) => {
|
|||||||
const [containedChar1, containedChar2] = getTwoCharsInPassword(targetServer.password);
|
const [containedChar1, containedChar2] = getTwoCharsInPassword(targetServer.password);
|
||||||
const hint = `The password for ${targetServer.hostname} contains ${containedChar1} and ${containedChar2}`;
|
const hint = `The password for ${targetServer.hostname} contains ${containedChar1} and ${containedChar2}`;
|
||||||
server.writeToTextFile(hintFileName, hint);
|
server.writeToTextFile(hintFileName, hint);
|
||||||
return;
|
files.push(hintFileName);
|
||||||
|
return files;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return files;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getClueFileName = (fileNameList: readonly string[]): TextFilePath => {
|
export const getClueFileName = (fileNameList: readonly string[]): TextFilePath => {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -43,7 +43,7 @@ export function Cyberpunk2077Game({ stage }: IProps): React.ReactElement {
|
|||||||
<Typography variant="h5" color={Settings.theme.primary}>
|
<Typography variant="h5" color={Settings.theme.primary}>
|
||||||
Targets:{" "}
|
Targets:{" "}
|
||||||
{stage.answers.map((a, i) => {
|
{stage.answers.map((a, i) => {
|
||||||
if (i == stage.currentAnswerIndex)
|
if (i === stage.currentAnswerIndex)
|
||||||
return (
|
return (
|
||||||
<span key={`${i}`} style={{ fontSize: "1em", color: Settings.theme.infolight }}>
|
<span key={`${i}`} style={{ fontSize: "1em", color: Settings.theme.infolight }}>
|
||||||
{a}
|
{a}
|
||||||
@@ -64,20 +64,32 @@ export function Cyberpunk2077Game({ stage }: IProps): React.ReactElement {
|
|||||||
gap: 1,
|
gap: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{flatGrid.map((item, idx) => (
|
{flatGrid.map((item, idx) => {
|
||||||
|
let borderStyle = "solid";
|
||||||
|
let borderColor = "transparent";
|
||||||
|
if (item.selected) {
|
||||||
|
borderColor = Settings.theme.infolight;
|
||||||
|
} else if (hasAugment && item.content === stage.answers[stage.currentAnswerIndex]) {
|
||||||
|
borderStyle = "dashed";
|
||||||
|
borderColor = Settings.theme.warning;
|
||||||
|
}
|
||||||
|
return (
|
||||||
<Typography
|
<Typography
|
||||||
key={idx}
|
key={idx}
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
color: item.color,
|
color: item.color,
|
||||||
border: item.selected ? `2px solid ${Settings.theme.infolight}` : "unset",
|
// Always use a 2px border to avoid small symbol position shifts caused by size changes (element vs
|
||||||
|
// element + 2px border).
|
||||||
|
border: `2px ${borderStyle} ${borderColor}`,
|
||||||
lineHeight: "unset",
|
lineHeight: "unset",
|
||||||
p: item.selected ? "2px" : "4px",
|
p: item.selected ? "2px" : "4px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.content}
|
{item.content}
|
||||||
</Typography>
|
</Typography>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function JobRoot(): React.ReactElement {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Box key={companyName} sx={{ marginBottom: "20px" }}>
|
<Box key={companyName} sx={{ marginBottom: "20px" }}>
|
||||||
<GenericLocation location={location} showBackButton={false} />;
|
<GenericLocation location={location} showBackButton={false} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,20 +8,12 @@ import { workerScripts } from "./WorkerScripts";
|
|||||||
|
|
||||||
import { GetAllServers, GetServer } from "../Server/AllServers";
|
import { GetAllServers, GetServer } from "../Server/AllServers";
|
||||||
import { AddRecentScript } from "./RecentScripts";
|
import { AddRecentScript } from "./RecentScripts";
|
||||||
import { ITutorial } from "../InteractiveTutorial";
|
|
||||||
import { AlertEvents } from "../ui/React/AlertManager";
|
|
||||||
import { handleUnknownError } from "../utils/ErrorHandler";
|
import { handleUnknownError } from "../utils/ErrorHandler";
|
||||||
import { roundToTwo } from "../utils/helpers/roundToTwo";
|
import { roundToTwo } from "../utils/helpers/roundToTwo";
|
||||||
import { BaseServer } from "../Server/BaseServer";
|
import { BaseServer } from "../Server/BaseServer";
|
||||||
|
|
||||||
export function killWorkerScript(ws: WorkerScript): boolean {
|
export function killWorkerScript(ws: WorkerScript): void {
|
||||||
if (ITutorial.isRunning) {
|
|
||||||
AlertEvents.emit("Processes cannot be killed during the tutorial.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
stopAndCleanUpWorkerScript(ws);
|
stopAndCleanUpWorkerScript(ws);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function killWorkerScriptByPid(pid: number, killer?: WorkerScript): boolean {
|
export function killWorkerScriptByPid(pid: number, killer?: WorkerScript): boolean {
|
||||||
|
|||||||
@@ -675,18 +675,15 @@ export const ns: InternalAPI<NSFull> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
helpers.log(ctx, () => "About to exit...");
|
helpers.log(ctx, () => "About to exit...");
|
||||||
const killed = killWorkerScript(ctx.workerScript);
|
killWorkerScript(ctx.workerScript);
|
||||||
|
|
||||||
if (runOpts.spawnDelay === 0) {
|
if (runOpts.spawnDelay === 0) {
|
||||||
helpers.log(ctx, () => `Executing '${path}' immediately`);
|
helpers.log(ctx, () => `Executing '${path}' immediately`);
|
||||||
spawnCb();
|
spawnCb();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (killed) {
|
|
||||||
// This prevents error messages about statements after the spawn()
|
// This prevents error messages about statements after the spawn()
|
||||||
// trying to be executed when the script is dead.
|
// trying to be executed when the script is dead.
|
||||||
throw new ScriptDeath(ctx.workerScript);
|
throw new ScriptDeath(ctx.workerScript);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
self: (ctx) => () => {
|
self: (ctx) => () => {
|
||||||
const runningScript = helpers.getRunningScript(ctx, ctx.workerScript.pid);
|
const runningScript = helpers.getRunningScript(ctx, ctx.workerScript.pid);
|
||||||
|
|||||||
+2
-2
@@ -6296,7 +6296,7 @@ interface HackingFormulas {
|
|||||||
* Calculate the security decrease from a weaken operation.
|
* Calculate the security decrease from a weaken operation.
|
||||||
* Unlike other hacking formulas, weaken effect depends only on thread count and
|
* Unlike other hacking formulas, weaken effect depends only on thread count and
|
||||||
* core count, not on server or player properties. The core bonus formula is
|
* core count, not on server or player properties. The core bonus formula is
|
||||||
* `1 + (cores - 1) / 16}`.
|
* `1 + (cores - 1) / 16`.
|
||||||
* @param threads - Number of threads running weaken.
|
* @param threads - Number of threads running weaken.
|
||||||
* @param cores - Number of cores on the host server. Default 1.
|
* @param cores - Number of cores on the host server. Default 1.
|
||||||
* @returns The security decrease amount.
|
* @returns The security decrease amount.
|
||||||
@@ -7950,7 +7950,7 @@ export interface NS {
|
|||||||
* @param threadOrOptions - Either an integer number of threads for new script, or a {@link SpawnOptions} object. Threads defaults to 1 and spawnDelay defaults to 10,000 ms.
|
* @param threadOrOptions - Either an integer number of threads for new script, or a {@link SpawnOptions} object. Threads defaults to 1 and spawnDelay defaults to 10,000 ms.
|
||||||
* @param args - Additional arguments to pass into the new script that is being run.
|
* @param args - Additional arguments to pass into the new script that is being run.
|
||||||
*/
|
*/
|
||||||
spawn(script: string, threadOrOptions?: number | SpawnOptions, ...args: ScriptArg[]): void;
|
spawn(script: string, threadOrOptions?: number | SpawnOptions, ...args: ScriptArg[]): never;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the currently running script.
|
* Returns the currently running script.
|
||||||
|
|||||||
@@ -182,7 +182,12 @@ export function SidebarRoot(props: { page: Page }): React.ReactElement {
|
|||||||
|
|
||||||
const clickPage = useCallback(
|
const clickPage = useCallback(
|
||||||
(page: Page) => {
|
(page: Page) => {
|
||||||
if (page == Page.ScriptEditor || page == Page.Documentation || page == Page.Options) {
|
if (page == Page.ScriptEditor) {
|
||||||
|
Router.toPage(page, {
|
||||||
|
files: new Map(),
|
||||||
|
options: { vim: Settings.MonacoDefaultToVim, hostname: Player.currentServer },
|
||||||
|
});
|
||||||
|
} else if (page == Page.Documentation || page == Page.Options) {
|
||||||
Router.toPage(page, {});
|
Router.toPage(page, {});
|
||||||
} else if (isSimplePage(page)) {
|
} else if (isSimplePage(page)) {
|
||||||
Router.toPage(page);
|
Router.toPage(page);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Terminal } from "../../../Terminal";
|
import { Terminal } from "../../../Terminal";
|
||||||
import { ScriptEditorRouteOptions, Page } from "../../../ui/Router";
|
import { Page } from "../../../ui/Router";
|
||||||
import { Router } from "../../../ui/GameRoot";
|
import { Router } from "../../../ui/GameRoot";
|
||||||
import { BaseServer } from "../../../Server/BaseServer";
|
import type { BaseServer } from "../../../Server/BaseServer";
|
||||||
import { type ScriptFilePath, hasScriptExtension, isLegacyScript } from "../../../Paths/ScriptFilePath";
|
import { type ScriptFilePath, hasScriptExtension, isLegacyScript } from "../../../Paths/ScriptFilePath";
|
||||||
import { TextFilePath, hasTextExtension } from "../../../Paths/TextFilePath";
|
import { type TextFilePath, hasTextExtension } from "../../../Paths/TextFilePath";
|
||||||
import { getGlobbedFileMap } from "../../../Paths/GlobbedFiles";
|
import { getGlobbedFileMap } from "../../../Paths/GlobbedFiles";
|
||||||
import { sendDeprecationNotice } from "./deprecation";
|
import { sendDeprecationNotice } from "./deprecation";
|
||||||
import { getFileType, getFileTypeFeature } from "../../../utils/ScriptTransformer";
|
import { getFileType, getFileTypeFeature } from "../../../utils/ScriptTransformer";
|
||||||
@@ -14,6 +14,7 @@ import { hasCacheExtension } from "../../../Paths/CacheFilePath";
|
|||||||
interface EditorParameters {
|
interface EditorParameters {
|
||||||
args: (string | number | boolean)[];
|
args: (string | number | boolean)[];
|
||||||
server: BaseServer;
|
server: BaseServer;
|
||||||
|
vim: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getScriptTemplate(path: string): string {
|
function getScriptTemplate(path: string): string {
|
||||||
@@ -33,11 +34,7 @@ export async function main(ns) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function commonEditor(
|
export function commonEditor(command: string, { args, server, vim }: EditorParameters): void {
|
||||||
command: string,
|
|
||||||
{ args, server }: EditorParameters,
|
|
||||||
options?: ScriptEditorRouteOptions,
|
|
||||||
): void {
|
|
||||||
if (args.length < 1) return Terminal.error(`Incorrect usage of ${command} command. Usage: ${command} [scriptname]`);
|
if (args.length < 1) return Terminal.error(`Incorrect usage of ${command} command. Usage: ${command} [scriptname]`);
|
||||||
const files = new Map<ScriptFilePath | TextFilePath, string>();
|
const files = new Map<ScriptFilePath | TextFilePath, string>();
|
||||||
let hasLegacyScript = false;
|
let hasLegacyScript = false;
|
||||||
@@ -76,5 +73,5 @@ export function commonEditor(
|
|||||||
if (hasLegacyScript) {
|
if (hasLegacyScript) {
|
||||||
sendDeprecationNotice();
|
sendDeprecationNotice();
|
||||||
}
|
}
|
||||||
Router.toPage(Page.ScriptEditor, { files, options });
|
Router.toPage(Page.ScriptEditor, { files, options: { vim, hostname: server.hostname } });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BaseServer } from "../../Server/BaseServer";
|
import type { BaseServer } from "../../Server/BaseServer";
|
||||||
|
|
||||||
import { commonEditor } from "./common/editor";
|
import { commonEditor } from "./common/editor";
|
||||||
|
|
||||||
export function nano(args: (string | number | boolean)[], server: BaseServer): void {
|
export function nano(args: (string | number | boolean)[], server: BaseServer): void {
|
||||||
return commonEditor("nano", { args, server }, { vim: false });
|
return commonEditor("nano", { args, server, vim: false });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BaseServer } from "../../Server/BaseServer";
|
import type { BaseServer } from "../../Server/BaseServer";
|
||||||
|
|
||||||
import { commonEditor } from "./common/editor";
|
import { commonEditor } from "./common/editor";
|
||||||
|
|
||||||
export function vim(args: (string | number | boolean)[], server: BaseServer): void {
|
export function vim(args: (string | number | boolean)[], server: BaseServer): void {
|
||||||
return commonEditor("vim", { args, server }, { vim: true });
|
return commonEditor("vim", { args, server, vim: true });
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -72,7 +72,6 @@ import { V2Modal } from "../utils/V2Modal";
|
|||||||
import { useRerender } from "./React/hooks";
|
import { useRerender } from "./React/hooks";
|
||||||
import { HistoryProvider } from "./React/Documentation";
|
import { HistoryProvider } from "./React/Documentation";
|
||||||
import { GoRoot } from "../Go/ui/GoRoot";
|
import { GoRoot } from "../Go/ui/GoRoot";
|
||||||
import { Settings } from "../Settings/Settings";
|
|
||||||
import { isBitNodeFinished } from "../BitNode/BitNodeUtils";
|
import { isBitNodeFinished } from "../BitNode/BitNodeUtils";
|
||||||
import { UIEventEmitter, UIEventType } from "./UIEventEmitter";
|
import { UIEventEmitter, UIEventType } from "./UIEventEmitter";
|
||||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||||
@@ -351,9 +350,9 @@ export function GameRoot(): React.ReactElement {
|
|||||||
case Page.ScriptEditor: {
|
case Page.ScriptEditor: {
|
||||||
mainPage = (
|
mainPage = (
|
||||||
<ScriptEditorRoot
|
<ScriptEditorRoot
|
||||||
files={pageWithContext.files ?? new Map()}
|
files={pageWithContext.files}
|
||||||
hostname={pageWithContext.options?.hostname ?? Player.getCurrentServer().hostname}
|
hostname={pageWithContext.options.hostname}
|
||||||
vim={pageWithContext.options === undefined ? Settings.MonacoDefaultToVim : pageWithContext.options.vim}
|
vim={pageWithContext.options.vim}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ export function LoadingScreen(): React.ReactElement {
|
|||||||
<Grid item>
|
<Grid item>
|
||||||
<Typography>
|
<Typography>
|
||||||
If the game fails to load, consider <a href="?noScripts">killing all scripts</a>
|
If the game fails to load, consider <a href="?noScripts">killing all scripts</a>
|
||||||
|
<br />
|
||||||
|
You can export your save data at <a href="./export.html">export.html</a>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+2
-2
@@ -18,7 +18,7 @@ export type PageContext<T extends Page> = T extends ComplexPage.BitVerse
|
|||||||
: T extends ComplexPage.FactionAugmentations
|
: T extends ComplexPage.FactionAugmentations
|
||||||
? { faction: Faction }
|
? { faction: Faction }
|
||||||
: T extends ComplexPage.ScriptEditor
|
: T extends ComplexPage.ScriptEditor
|
||||||
? { files?: Map<ScriptFilePath | TextFilePath, string>; options?: ScriptEditorRouteOptions }
|
? { files: Map<ScriptFilePath | TextFilePath, string>; options: ScriptEditorRouteOptions }
|
||||||
: T extends ComplexPage.Location
|
: T extends ComplexPage.Location
|
||||||
? { location: Location }
|
? { location: Location }
|
||||||
: T extends ComplexPage.ImportSave
|
: T extends ComplexPage.ImportSave
|
||||||
@@ -43,7 +43,7 @@ export type PageWithContext =
|
|||||||
|
|
||||||
export interface ScriptEditorRouteOptions {
|
export interface ScriptEditorRouteOptions {
|
||||||
vim: boolean;
|
vim: boolean;
|
||||||
hostname?: string;
|
hostname: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The router keeps track of player navigation/routing within the game. */
|
/** The router keeps track of player navigation/routing within the game. */
|
||||||
|
|||||||
@@ -22,3 +22,22 @@ if (window.indexedDB) {
|
|||||||
writable: false,
|
writable: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some players use really old browser versions on unsupported OSes such as Windows 7. Intl.Segmenter and other APIs are
|
||||||
|
// not supported in these browsers, so they will only see a black screen when loading the game. We should show an alert
|
||||||
|
// to notify them that they should update their browser, if possible.
|
||||||
|
if (typeof Intl.Segmenter !== "function" || typeof String.prototype.toWellFormed !== "function") {
|
||||||
|
const errorMessage = `Your browser is too outdated. Please update your browser.\n\nUserAgent: ${navigator.userAgent}`;
|
||||||
|
alert(errorMessage);
|
||||||
|
const rootElement = document.getElementById("root");
|
||||||
|
if (rootElement) {
|
||||||
|
rootElement.innerText = errorMessage;
|
||||||
|
rootElement.style.color = "red";
|
||||||
|
rootElement.style.fontSize = "20px";
|
||||||
|
rootElement.style.justifyContent = "center";
|
||||||
|
}
|
||||||
|
// If the browser does not support Intl.Segmenter, initialization of graphemeSegmenter in StringHelperFunctions.ts
|
||||||
|
// will fail, so this throw is technically unnecessary. However, if toWellFormed is not supported, the game can still
|
||||||
|
// load normally while darknet initialization fails, potentially causing UI issues such as an empty darknet tab.
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ mkdir .app
|
|||||||
|
|
||||||
# Should be all the files needed.
|
# Should be all the files needed.
|
||||||
cp index.html .app
|
cp index.html .app
|
||||||
|
cp export.html .app
|
||||||
cp favicon.ico .app
|
cp favicon.ico .app
|
||||||
cp -r dist .app
|
cp -r dist .app
|
||||||
|
|||||||
Reference in New Issue
Block a user