mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-22 07:33:48 +01:00
commit
5874fff0bd
@ -43,7 +43,7 @@ module.exports = {
|
||||
curly: ["off"],
|
||||
"default-case": ["off"],
|
||||
"dot-notation": ["off"],
|
||||
"eol-last": ["off"],
|
||||
"eol-last": ["error"],
|
||||
eqeqeq: ["off"],
|
||||
"for-direction": ["error"],
|
||||
"func-call-spacing": ["off"],
|
||||
@ -236,7 +236,7 @@ module.exports = {
|
||||
"no-ternary": ["off"],
|
||||
"no-this-before-super": ["off"],
|
||||
"no-throw-literal": ["error"],
|
||||
"no-trailing-spaces": ["off"],
|
||||
"no-trailing-spaces": ["error"],
|
||||
"no-undef": ["off"],
|
||||
"no-undef-init": ["error"],
|
||||
"no-undefined": ["off"],
|
||||
|
1
CODE_OF_CONDUCT.md
Normal file
1
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1 @@
|
||||
Don't be an ass.
|
2
dist/bitburner.d.ts
vendored
2
dist/bitburner.d.ts
vendored
@ -3915,7 +3915,7 @@ export declare interface NS extends Singularity {
|
||||
* @param data - Data to write.
|
||||
* @returns True if the data is successfully written to the port, and false otherwise.
|
||||
*/
|
||||
tryWritePort(port: number, data: string[] | number): Promise<boolean>;
|
||||
tryWritePort(port: number, data: string | number): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Read content of a file.
|
||||
|
34
dist/vendor.bundle.js
vendored
34
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/vendor.bundle.js.map
vendored
2
dist/vendor.bundle.js.map
vendored
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@
|
||||
|
||||
Augmentations
|
||||
=============
|
||||
Advances in science and medicine have lead to powerful new technologies
|
||||
Advances in science and medicine have led to powerful new technologies
|
||||
that allow people to augment themselves beyond normal human capabilities.
|
||||
There are many different types of Augmentations, ranging from cybernetic
|
||||
to genetic to biological. Acquiring these Augmentations enhances the
|
||||
|
@ -26,7 +26,7 @@ async function initialize(win) {
|
||||
} else {
|
||||
log.log('Invalid authentication token');
|
||||
res.writeHead(401);
|
||||
|
||||
|
||||
res.end(JSON.stringify({
|
||||
success: false,
|
||||
msg: 'Invalid authentication token'
|
||||
@ -71,7 +71,7 @@ async function initialize(win) {
|
||||
|
||||
result = await window.webContents.executeJavaScript(`document.saveFile("${data.filename}", "${data.code}")`);
|
||||
break;
|
||||
|
||||
|
||||
// Delete files
|
||||
case "DELETE":
|
||||
result = await window.webContents.executeJavaScript(`document.deleteFile("${data.filename}")`);
|
||||
|
@ -312,6 +312,16 @@ function getMenu(window) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Reset Zoom",
|
||||
enabled: utils.getZoomFactor() !== 1,
|
||||
accelerator: "CommandOrControl+num0",
|
||||
click: () => {
|
||||
utils.setZoomFactor(window, 1);
|
||||
refreshMenu(window);
|
||||
log.log("Reset zoom");
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
137
license.txt
137
license.txt
@ -1,85 +1,88 @@
|
||||
LIMITED USE SOFTWARE LICENSE AGREEMENT
|
||||
“Commons Clause” License Condition v1.0
|
||||
|
||||
This Limited Use Software License Agreement (the "Agreement") is a legal agreement between you, the end-user, and Daniel Y Xie,
|
||||
the creator of the software, hereafter referred to as the Creator.
|
||||
The Software is provided to you by the Licensor under the License, as defined below, subject to the following condition.
|
||||
|
||||
By downloading or purchasing the software material, which includes source code (the "Source Code"), artwork data, music and software
|
||||
tools (collectively, the "Software"), you are agreeing to be bound by the terms of this Agreement. If you do not agree to the terms
|
||||
of this Agreement, promptly destroy the Software you may have downloaded or copied.
|
||||
Without limiting other conditions in the License, the grant of rights under the License will not include, and the License does not grant to you, the right to Sell the Software.
|
||||
|
||||
SOFTWARE LICENSE
|
||||
For purposes of the foregoing, “Sell” means practicing any or all of the rights granted to you under the License to provide to third parties, for a fee or other consideration (including without limitation fees for hosting or consulting/ support services related to the Software), a product or service whose value derives, entirely or substantially, from the functionality of the Software. Any license notice or attribution required by the License must also include this Commons Clause License Condition notice.
|
||||
|
||||
1. Grant of License. The Creator grants to you the right to use the Software. You have no ownership or proprietary rights in or to
|
||||
the Software, or the Trademark. For purposes of this section, "use" means loading the Software into RAM, as well as installation
|
||||
on a hard disk or other storage device. The Software, together with any archive copy thereof, shall be destroyed when no longer
|
||||
used in accordance with this Agreement, or when the right to use the Software is terminated. You agree that the Software will not
|
||||
be shipped, transferred or exported into any country in violation of the U.S. Export Administration Act (or any other law governing
|
||||
such matters) and that you will not utilize, in any other manner, the Software in violation of any applicable law.
|
||||
Software: Bitburner
|
||||
|
||||
2. Permitted Uses.
|
||||
License: Apache 2.0 with Commons Clause
|
||||
|
||||
You may copy and distribute literal (i.e., verbatim) or modified copies of the Software's source code.
|
||||
You must meet all of the following conditions with respect to any work that you distribute or publish that in whole or in part
|
||||
contains or is derived from the Software or any part thereof:
|
||||
Licensor: Daniel Y Xie
|
||||
|
||||
(a) The distributed Software may not be used in any way for commercial gain and must not violate any of the restrictions
|
||||
set forth in Section 3.
|
||||
(b) If you have modified the Software, you must cause the modified Software to carry prominent notices stating that
|
||||
you have modified the Software's files. Modifications must not alter or remove any copyright notices in the Software.
|
||||
(c) The distributed Software must include either a written copy of this License, or a prominent written indication that
|
||||
the Software is covered by this License and written instructions for printing and/or displaying the copy of the License
|
||||
on the distribution medium
|
||||
(d) When modifications to the Software are released under this license, a non-exclusive royalty-free right is granted to
|
||||
the initial developer of the Software to distribute your modification in future versions of the Software provided
|
||||
such versions remain available under these terms in addition to any other license(s) of the initial developer.
|
||||
(d) You may not impose any further restrictions on the recipient's exercise of the rights granted herein.
|
||||
-------------------------------------------------------------------
|
||||
|
||||
For educational purposes only, you, the end-user, may use portions of the Source Code, such as particular
|
||||
routines, to develop your own software, but may not duplicate the Source Code. The limited
|
||||
right referenced in the preceding sentence is hereinafter referred to as "Educational Use." By so exercising the Educational
|
||||
Use right you shall not obtain any ownership, copyright, proprietary or other interest in or to the Source Code, or any portion
|
||||
of the Source Code. You may dispose of your own software in your sole discretion. When exercising the Educational Use right,
|
||||
you may not use or exploit the Software, or any portion of the Software, which includes the Source Code, for commercial gain.
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
3. Prohibited Uses: Under no circumstances shall you, the end-user, be permitted, allowed or authorized to commercially
|
||||
exploit the Software or any work that in whole or in part contains or is derived from the Software or any part thereof.
|
||||
Neither you nor anyone at your direction shall do any of the following acts with regard to the Software,
|
||||
or any portion thereof:
|
||||
Definitions.
|
||||
|
||||
Rent;
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
Sell;
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||
|
||||
Lease;
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
Offer on a pay-per-play basis;
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
|
||||
Distribute for money or any other consideration; or
|
||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||
|
||||
In any other manner and through any medium whatsoever commercially exploit or use for any commercial purpose.
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||
|
||||
4. Copyright. The Software and all copyrights related thereto (including all characters and other images generated by the
|
||||
Software or depicted in the Software) are owned by the Creator and is protected by United States copyright laws and international treaty
|
||||
provisions. The Creator shall retain exclusive ownership and copyright in and to the Software and all portions of the Software and you
|
||||
shall have no ownership or other proprietary interest in such materials. You must treat the Software like any other copyrighted
|
||||
material. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License.
|
||||
You may not otherwise reproduce, copy or disclose to others, in whole or in any part, the Software. You may not copy the
|
||||
written materials accompanying the Software. You agree to use your best efforts to see that any user of the Software licensed
|
||||
hereunder complies with this Agreement.
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
5. NO WARRANTIES. The Creator DISCLAIMS ALL WARRANTIES, BOTH EXPRESS IMPLIED, INCLUDING BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE WITH RESPECT TO THE SOFTWARE. THIS LIMITED WARRANTY GIVES YOU SPECIFIC
|
||||
LEGAL RIGHTS. YOU MAY HAVE OTHER RIGHTS WHICH VARY FROM JURISDICTION TO JURISDICTION. the Creator DOES NOT WARRANT THAT THE OPERATION
|
||||
OF THE SOFTWARE WILL BE UNINTERRUPTED, ERROR FREE OR MEET YOUR SPECIFIC REQUIREMENTS. THE WARRANTY SET FORTH ABOVE IS IN LIEU
|
||||
OF ALL OTHER EXPRESS WARRANTIES WHETHER ORAL OR WRITTEN. THE AGENTS, EMPLOYEES, DISTRIBUTORS, AND DEALERS OF THE CREATOR ARE NOT AUTHORIZED
|
||||
TO MAKE MODIFICATIONS TO THIS WARRANTY, OR ADDITIONAL WARRANTIES ON BEHALF OF THE CREATOR.
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
Exclusive Remedies. The Software is being offered to you free of any charge. You agree that you have no remedy against the creator,
|
||||
its affiliates, contractors, suppliers, and agents for loss or damage caused by any defect or failure in the Software regardless
|
||||
of the form of action, whether in contract, tort, includinegligence, strict liability or otherwise, with regard to the Software.
|
||||
This Agreement shall be construed in accordance with and governed by the laws of the State of Texas. Copyright and other
|
||||
proprietary matters will be governed by United States laws and international treaties. IN ANY CASE, THE CREATOR SHALL NOT BE LIABLE
|
||||
FOR LOSS OF DATA, LOSS OF PROFITS, LOST SAVINGS, SPECIAL, INCIDENTAL, CONSEQUENTIAL, INDIRECT OR OTHER SIMILAR DAMAGES ARISING
|
||||
FROM BREACH OF WARRANTY, BREACH OF CONTRACT, NEGLIGENCE, OR OTHER LEGAL THEORY EVEN IF THE CREATOR OR ITS AGENT HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so the above limitation or exclusion may not apply to you.
|
||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
||||
|
||||
Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
||||
|
||||
Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
||||
|
||||
Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
||||
|
||||
Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
||||
|
||||
Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
||||
|
||||
Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 Daniel Y Xie
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -125,6 +125,6 @@
|
||||
"electron:packager-win": "electron-packager .package bitburner --platform win32 --arch x64 --out .build --overwrite --icon .package/icon.png",
|
||||
"electron:packager-mac": "electron-packager .package bitburner --platform darwin --arch x64 --out .build --overwrite --icon .package/icon.png",
|
||||
"electron:packager-linux": "electron-packager .package bitburner --platform linux --arch x64 --out .build --overwrite --icon .package/icon.png",
|
||||
"allbuild": "npm run build && npm run electron && git add --all && git commit -m 'allbuild commit `git rev-parse --short HEAD`' && git push -f -u origin dev"
|
||||
"allbuild": "npm run build && npm run electron && git add --all && git commit -m \"allbuild commit $(which git)\" && git push -f -u origin dev"
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ export function printAliases(): void {
|
||||
|
||||
// Returns true if successful, false otherwise
|
||||
export function parseAliasDeclaration(dec: string, global = false): boolean {
|
||||
const re = /^([\w|!|%|,|@|-]+)=(("(.+)")|('(.+)'))$/;
|
||||
const re = /^([\w|!%,@-]+)=(("(.+)")|('(.+)'))$/;
|
||||
const matches = dec.match(re);
|
||||
if (matches == null || matches.length != 7) {
|
||||
return false;
|
||||
|
43
src/Arcade/ui/ArcadeRoot.tsx
Normal file
43
src/Arcade/ui/ArcadeRoot.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React, { useState } from "react";
|
||||
import { BBCabinetRoot } from "./BBCabinet";
|
||||
|
||||
import Button from "@mui/material/Button";
|
||||
import { use } from "../../ui/Context";
|
||||
import { AlertEvents } from "../../ui/React/AlertManager";
|
||||
|
||||
enum Page {
|
||||
None,
|
||||
Megabyteburner2000,
|
||||
}
|
||||
|
||||
export function ArcadeRoot(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const [page, setPage] = useState(Page.None);
|
||||
|
||||
function mbBurner2000(): void {
|
||||
if (player.sourceFileLvl(1) === 0) {
|
||||
AlertEvents.emit("This machine is broken.");
|
||||
} else {
|
||||
setPage(Page.Megabyteburner2000);
|
||||
}
|
||||
}
|
||||
|
||||
if (page === Page.None) {
|
||||
return (
|
||||
<>
|
||||
<Button onClick={mbBurner2000}>Megabyte burner 2000</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
let currentGame = <></>;
|
||||
switch (page) {
|
||||
case Page.Megabyteburner2000:
|
||||
currentGame = <BBCabinetRoot />;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setPage(Page.None)}>Back</Button>
|
||||
{currentGame}
|
||||
</>
|
||||
);
|
||||
}
|
52
src/Arcade/ui/BBCabinet.tsx
Normal file
52
src/Arcade/ui/BBCabinet.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
const metaBB = "https://bitburner-official.github.io/bitburner-legacy/";
|
||||
|
||||
const style = {
|
||||
width: "1060px",
|
||||
height: "800px",
|
||||
border: "0px",
|
||||
} as any;
|
||||
|
||||
export function BBCabinetRoot(): React.ReactElement {
|
||||
// prettier-ignore
|
||||
const joystick =
|
||||
<>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ,'" "', .-. </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> / \ ( ) </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | .-. '-' .-. </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ / ( ) ( )</Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> '.___.' '-' .-. '-'</Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ||| ( ) </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ||| '-' </Typography>
|
||||
</>;
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
width: "1060px",
|
||||
height: "800px",
|
||||
padding: "0",
|
||||
overflow: "hidden",
|
||||
borderColor: "white",
|
||||
borderStyle: "solid",
|
||||
borderWidth: "5px",
|
||||
}}
|
||||
>
|
||||
<iframe src={metaBB} style={style} />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: "1060px",
|
||||
borderColor: "white",
|
||||
borderStyle: "solid",
|
||||
borderWidth: "5px",
|
||||
}}
|
||||
>
|
||||
{joystick}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -2598,12 +2598,7 @@ function augmentationExists(name: string): boolean {
|
||||
|
||||
export function isRepeatableAug(aug: Augmentation): boolean {
|
||||
const augName = aug instanceof Augmentation ? aug.name : aug;
|
||||
|
||||
if (augName === AugmentationNames.NeuroFluxGovernor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return augName === AugmentationNames.NeuroFluxGovernor;
|
||||
}
|
||||
|
||||
export { installAugmentations, initAugmentations, applyAugmentation, augmentationExists };
|
||||
|
@ -8,8 +8,8 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { use } from "../../ui/Context";
|
||||
|
@ -4,8 +4,8 @@
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
|
||||
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
||||
|
@ -132,7 +132,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const enter = enterBitNode;
|
||||
const destroyed = player.bitNodeN;
|
||||
const [destroySequence, setDestroySequence] = useState(true && !props.quick);
|
||||
const [destroySequence, setDestroySequence] = useState(!props.quick);
|
||||
|
||||
// Update NextSourceFileFlags
|
||||
const nextSourceFileFlags = SourceFileFlags.slice();
|
||||
|
@ -1914,6 +1914,9 @@ export class Bladeburner implements IBladeburner {
|
||||
}
|
||||
|
||||
process(router: IRouter, player: IPlayer): void {
|
||||
// Edge race condition when the engine checks the processing counters and attempts to route before the router is initialized.
|
||||
if (!router.isInitialized) return;
|
||||
|
||||
// Edge case condition...if Operation Daedalus is complete trigger the BitNode
|
||||
if (router.page() !== Page.BitVerse && this.blackops.hasOwnProperty("Operation Daedalus")) {
|
||||
return router.toBitVerse(false, false);
|
||||
|
@ -16,4 +16,4 @@ for (const actionName of actionNames){
|
||||
GeneralActions[actionName] = new Action({
|
||||
name: actionName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ export function OperationPage(props: IProps): React.ReactElement {
|
||||
between different Operations.
|
||||
<br />
|
||||
<br />
|
||||
For operations, you can use a team. You must first recruit team members. Having a larger team will improves your
|
||||
For operations, you can use a team. You must first recruit team members. Having a larger team will improve your
|
||||
chances of success.
|
||||
<br />
|
||||
<br />
|
||||
|
@ -74,7 +74,7 @@ for (var i = 0; i < multKeys.length; ++i) {
|
||||
if (mult && mult !== 1) {
|
||||
mult = formatNumber(mult, 3);
|
||||
switch(multKeys[i]) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,9 @@ import { ResearchMap } from "./ResearchMap";
|
||||
import { isRelevantMaterial } from "./ui/Helpers";
|
||||
|
||||
export function NewIndustry(corporation: ICorporation, industry: string, name: string): void {
|
||||
if (corporation.divisions.find(({ type }) => industry == type))
|
||||
throw new Error(`You have already expanded into the ${industry} industry!`);
|
||||
|
||||
for (let i = 0; i < corporation.divisions.length; ++i) {
|
||||
if (corporation.divisions[i].name === name) {
|
||||
throw new Error("This division name is already in use!");
|
||||
@ -111,8 +114,8 @@ export function SellMaterial(mat: Material, amt: string, price: string): void {
|
||||
if (amt.includes("MAX") || amt.includes("PROD")) {
|
||||
let q = amt.replace(/\s+/g, "");
|
||||
q = q.replace(/[^-()\d/*+.MAXPROD]/g, "");
|
||||
let tempQty = q.replace(/MAX/g, "1");
|
||||
tempQty = tempQty.replace(/PROD/g, "1");
|
||||
let tempQty = q.replace(/MAX/g, mat.maxsll.toString());
|
||||
tempQty = tempQty.replace(/PROD/g, mat.prd.toString());
|
||||
try {
|
||||
tempQty = eval(tempQty);
|
||||
} catch (e) {
|
||||
@ -176,8 +179,8 @@ export function SellProduct(product: Product, city: string, amt: string, price:
|
||||
//Dynamically evaluated quantity. First test to make sure its valid
|
||||
let qty = amt.replace(/\s+/g, "");
|
||||
qty = qty.replace(/[^-()\d/*+.MAXPROD]/g, "");
|
||||
let temp = qty.replace(/MAX/g, "1");
|
||||
temp = temp.replace(/PROD/g, "1");
|
||||
let temp = qty.replace(/MAX/g, product.maxsll.toString());
|
||||
temp = temp.replace(/PROD/g, product.data[city][1].toString());
|
||||
try {
|
||||
temp = eval(temp);
|
||||
} catch (e) {
|
||||
@ -291,7 +294,7 @@ export function BuyBackShares(corporation: ICorporation, player: IPlayer, numSha
|
||||
if (numShares > corporation.issuedShares) throw new Error("You don't have that many shares to buy!");
|
||||
if (!corporation.public) throw new Error("You haven't gone public!");
|
||||
const buybackPrice = corporation.sharePrice * 1.1;
|
||||
if (corporation.funds < (numShares * buybackPrice)) throw new Error("You cant afford that many shares!");
|
||||
if (player.money < (numShares * buybackPrice)) throw new Error("You cant afford that many shares!");
|
||||
corporation.numShares += numShares;
|
||||
corporation.issuedShares -= numShares;
|
||||
player.loseMoney(numShares * buybackPrice, "corporation");
|
||||
|
@ -828,7 +828,7 @@ export class Industry implements IIndustry {
|
||||
}
|
||||
}
|
||||
|
||||
const maxSell =
|
||||
mat.maxsll =
|
||||
(mat.qlt + 0.001) *
|
||||
marketFactor *
|
||||
markup *
|
||||
@ -839,7 +839,7 @@ export class Industry implements IIndustry {
|
||||
let sellAmt;
|
||||
if (isString(mat.sllman[1])) {
|
||||
//Dynamically evaluated
|
||||
let tmp = (mat.sllman[1] as string).replace(/MAX/g, (maxSell + "").toUpperCase());
|
||||
let tmp = (mat.sllman[1] as string).replace(/MAX/g, (mat.maxsll + "").toUpperCase());
|
||||
tmp = tmp.replace(/PROD/g, mat.prd + "");
|
||||
try {
|
||||
sellAmt = eval(tmp);
|
||||
@ -856,13 +856,13 @@ export class Industry implements IIndustry {
|
||||
);
|
||||
sellAmt = 0;
|
||||
}
|
||||
sellAmt = Math.min(maxSell, sellAmt);
|
||||
sellAmt = Math.min(mat.maxsll, sellAmt);
|
||||
} else if (mat.sllman[1] === -1) {
|
||||
//Backwards compatibility, -1 = MAX
|
||||
sellAmt = maxSell;
|
||||
sellAmt = mat.maxsll;
|
||||
} else {
|
||||
//Player's input value is just a number
|
||||
sellAmt = Math.min(maxSell, mat.sllman[1] as number);
|
||||
sellAmt = Math.min(mat.maxsll, mat.sllman[1] as number);
|
||||
}
|
||||
|
||||
sellAmt = sellAmt * CorporationConstants.SecsPerMarketCycle * marketCycles;
|
||||
@ -1188,8 +1188,7 @@ export class Industry implements IIndustry {
|
||||
}
|
||||
}
|
||||
|
||||
const maxSell =
|
||||
0.5 *
|
||||
product.maxsll = 0.5 *
|
||||
Math.pow(product.rat, 0.65) *
|
||||
marketFactor *
|
||||
corporation.getSalesMultiplier() *
|
||||
@ -1200,7 +1199,7 @@ export class Industry implements IIndustry {
|
||||
let sellAmt;
|
||||
if (product.sllman[city][0] && isString(product.sllman[city][1])) {
|
||||
//Sell amount is dynamically evaluated
|
||||
let tmp = product.sllman[city][1].replace(/MAX/g, (maxSell + "").toUpperCase());
|
||||
let tmp = product.sllman[city][1].replace(/MAX/g, (product.maxsll + "").toUpperCase());
|
||||
tmp = tmp.replace(/PROD/g, product.data[city][1]);
|
||||
try {
|
||||
tmp = eval(tmp);
|
||||
@ -1214,16 +1213,16 @@ export class Industry implements IIndustry {
|
||||
city +
|
||||
" office. Sell price is being set to MAX",
|
||||
);
|
||||
tmp = maxSell;
|
||||
tmp = product.maxsll;
|
||||
}
|
||||
sellAmt = Math.min(maxSell, tmp);
|
||||
sellAmt = Math.min(product.maxsll, tmp);
|
||||
} else if (product.sllman[city][0] && product.sllman[city][1] > 0) {
|
||||
//Sell amount is manually limited
|
||||
sellAmt = Math.min(maxSell, product.sllman[city][1]);
|
||||
sellAmt = Math.min(product.maxsll, product.sllman[city][1]);
|
||||
} else if (product.sllman[city][0] === false) {
|
||||
sellAmt = 0;
|
||||
} else {
|
||||
sellAmt = maxSell;
|
||||
sellAmt = product.maxsll;
|
||||
}
|
||||
if (sellAmt < 0) {
|
||||
sellAmt = 0;
|
||||
@ -1286,7 +1285,7 @@ export class Industry implements IIndustry {
|
||||
this.awareness = Math.min(awareness, Number.MAX_VALUE);
|
||||
|
||||
const popularity = (this.popularity + (1 * advMult)) * ((1 + getRandomInt(1, 3) / 100) * advMult);
|
||||
this.popularity = Math.min(popularity, Number.MAX_VALUE);
|
||||
this.popularity = Math.min(popularity, Number.MAX_VALUE);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -62,6 +62,9 @@ export class Material {
|
||||
marketTa2 = false;
|
||||
marketTa2Price = 0;
|
||||
|
||||
// Determines the maximum amount of this material that can be sold in one market cycle
|
||||
maxsll = 0;
|
||||
|
||||
constructor(params: IConstructorParams = {}) {
|
||||
if (params.name) {
|
||||
this.name = params.name;
|
||||
|
@ -186,8 +186,7 @@ export class OfficeSpace {
|
||||
jobCount--;
|
||||
}
|
||||
}
|
||||
if (jobCount !== amount) return false;
|
||||
return true;
|
||||
return jobCount === amount;
|
||||
}
|
||||
|
||||
toJSON(): any {
|
||||
|
@ -96,6 +96,8 @@ export class Product {
|
||||
marketTa2 = false;
|
||||
marketTa2Price: IMap<number> = createCityMap<number>(0);
|
||||
|
||||
// Determines the maximum amount of this product that can be sold in one market cycle
|
||||
maxsll = 0;
|
||||
constructor(params: IConstructorParams = {}) {
|
||||
this.name = params.name ? params.name : "";
|
||||
this.dmd = params.demand ? params.demand : 0;
|
||||
|
@ -6,6 +6,7 @@ import { IIndustry } from "../IIndustry";
|
||||
import { ExportMaterial } from "../Actions";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { useCorporation } from "./Context";
|
||||
import { isRelevantMaterial } from "./Helpers";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Button from "@mui/material/Button";
|
||||
@ -22,11 +23,13 @@ interface IProps {
|
||||
// Create a popup that lets the player manage exports
|
||||
export function ExportModal(props: IProps): React.ReactElement {
|
||||
const corp = useCorporation();
|
||||
if (corp.divisions.length === 0) throw new Error("Export popup created with no divisions.");
|
||||
if (Object.keys(corp.divisions[0].warehouses).length === 0)
|
||||
const possibleDivisions = corp.divisions.filter((division: IIndustry) => isRelevantMaterial(props.mat.name, division));
|
||||
if (possibleDivisions.length === 0) throw new Error("Export popup created with no divisions.");
|
||||
const defaultDivision = possibleDivisions[0];
|
||||
if (Object.keys(defaultDivision.warehouses).length === 0)
|
||||
throw new Error("Export popup created in a division with no warehouses.");
|
||||
const [industry, setIndustry] = useState<string>(corp.divisions[0].name);
|
||||
const [city, setCity] = useState<string>(Object.keys(corp.divisions[0].warehouses)[0]);
|
||||
const [industry, setIndustry] = useState<string>(defaultDivision.name);
|
||||
const [city, setCity] = useState<string>(Object.keys(defaultDivision.warehouses)[0]);
|
||||
const [amt, setAmt] = useState("");
|
||||
const setRerender = useState(false)[1];
|
||||
|
||||
@ -84,10 +87,12 @@ export function ExportModal(props: IProps): React.ReactElement {
|
||||
second. You can set the export amount to 'MAX' to export all of the materials in this warehouse.
|
||||
</Typography>
|
||||
<Select onChange={onIndustryChange} value={industry}>
|
||||
{corp.divisions.map((division: IIndustry) => (
|
||||
<MenuItem key={division.name} value={division.name}>
|
||||
{division.name}
|
||||
</MenuItem>
|
||||
{corp.divisions
|
||||
.filter((division: IIndustry) => isRelevantMaterial(props.mat.name, division))
|
||||
.map((division: IIndustry) => (
|
||||
<MenuItem key={division.name} value={division.name}>
|
||||
{division.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<Select onChange={onCityChange} value={city}>
|
||||
|
@ -4,7 +4,7 @@ import { IIndustry } from "../IIndustry";
|
||||
// current industry.
|
||||
export function isRelevantMaterial(matName: string, division: IIndustry): boolean {
|
||||
// Materials that affect Production multiplier
|
||||
const prodMultiplierMats = ["Hardware", "Robots", "AICores", "RealEstate"];
|
||||
const prodMultiplierMats = ["Hardware", "Robots", "AICores", "RealEstate", "AI Cores", "Real Estate"];
|
||||
|
||||
if (Object.keys(division.reqMats).includes(matName)) {
|
||||
return true;
|
||||
|
@ -95,8 +95,10 @@ function WarehouseRoot(props: IProps): React.ReactElement {
|
||||
const mats = [];
|
||||
for (const matName of Object.keys(props.warehouse.materials)) {
|
||||
if (!(props.warehouse.materials[matName] instanceof Material)) continue;
|
||||
// Only create UI for materials that are relevant for the industry
|
||||
if (!isRelevantMaterial(matName, division)) continue;
|
||||
// Only create UI for materials that are relevant for the industry or in stock
|
||||
const isInStock = props.warehouse.materials[matName].qty > 0;
|
||||
const isRelevant = isRelevantMaterial(matName, division);
|
||||
if (!isInStock && !isRelevant) continue;
|
||||
mats.push(
|
||||
<MaterialElem
|
||||
rerender={props.rerender}
|
||||
|
@ -20,14 +20,7 @@ export function determineCrimeSuccess(p: IPlayer, type: string): boolean {
|
||||
dialogBoxCreate(`ERR: Unrecognized crime type: ${type} This is probably a bug please contact the developer`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Math.random() <= chance) {
|
||||
//Success
|
||||
return true;
|
||||
} else {
|
||||
//Failure
|
||||
return false;
|
||||
}
|
||||
return Math.random() <= chance;
|
||||
}
|
||||
|
||||
export function findCrime(roughName: string): Crime | null {
|
||||
|
@ -6,8 +6,7 @@ export let LastExportBonus = 0;
|
||||
const bonusTimer = 24 * 60 * 60 * 1000; // 24h
|
||||
export function canGetBonus(): boolean {
|
||||
const now = new Date().getTime();
|
||||
if (now - LastExportBonus > bonusTimer) return true;
|
||||
return false;
|
||||
return now - LastExportBonus > bonusTimer;
|
||||
}
|
||||
|
||||
export function onExport(p: IPlayer): void {
|
||||
|
@ -559,7 +559,7 @@ export const FactionInfos: IMap<FactionInfo> = {
|
||||
{" `..` "}<br /><br />
|
||||
Many cultures predict an end to humanity in the near future, a final
|
||||
Armageddon that will end the world; but we disagree.
|
||||
<br /><br />Note that for this faction, reputation can
|
||||
<br /><br />Note that for this faction, reputation can
|
||||
only be gained by charging Stanek's gift.</>,
|
||||
[],
|
||||
false,
|
||||
|
@ -7,7 +7,7 @@ import { PurchaseableAugmentation } from "./PurchaseableAugmentation";
|
||||
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { Faction } from "../Faction";
|
||||
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { hasAugmentationPrereqs } from "../FactionHelpers";
|
||||
|
@ -4,7 +4,7 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { Faction } from "../Faction";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { repFromDonation } from "../formulas/donation";
|
||||
import { Favor } from "../../ui/React/Favor";
|
||||
|
@ -13,7 +13,7 @@ import { Option } from "./Option";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { Faction } from "../Faction";
|
||||
|
||||
import { use } from "../../ui/Context";
|
||||
import { CreateGangModal } from "./CreateGangModal";
|
||||
@ -24,6 +24,7 @@ import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPur
|
||||
|
||||
type IProps = {
|
||||
faction: Faction;
|
||||
augPage: boolean;
|
||||
};
|
||||
|
||||
// Info text for all options on the UI
|
||||
@ -70,7 +71,6 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
|
||||
const router = use.Router();
|
||||
const [sleevesOpen, setSleevesOpen] = useState(false);
|
||||
const [gangOpen, setGangOpen] = useState(false);
|
||||
const p = player;
|
||||
const factionInfo = faction.getInfo();
|
||||
|
||||
function manageGang(): void {
|
||||
@ -104,20 +104,20 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
|
||||
|
||||
// We have a special flag for whether the player this faction is the player's
|
||||
// gang faction because if the player has a gang, they cannot do any other action
|
||||
const isPlayersGang = p.inGang() && p.getGangName() === faction.name;
|
||||
const isPlayersGang = player.inGang() && player.getGangName() === faction.name;
|
||||
|
||||
// Flags for whether special options (gang, sleeve purchases, donate, etc.)
|
||||
// should be shown
|
||||
const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
|
||||
const canDonate = faction.favor >= favorToDonate;
|
||||
|
||||
const canPurchaseSleeves = faction.name === "The Covenant" && p.bitNodeN === 10;
|
||||
const canPurchaseSleeves = faction.name === "The Covenant" && player.bitNodeN === 10;
|
||||
|
||||
let canAccessGang = p.canAccessGang() && GangNames.includes(faction.name);
|
||||
if (p.inGang()) {
|
||||
if (p.getGangName() !== faction.name) {
|
||||
let canAccessGang = player.canAccessGang() && GangNames.includes(faction.name);
|
||||
if (player.inGang()) {
|
||||
if (player.getGangName() !== faction.name) {
|
||||
canAccessGang = false;
|
||||
} else if (p.getGangName() === faction.name) {
|
||||
} else if (player.getGangName() === faction.name) {
|
||||
canAccessGang = true;
|
||||
}
|
||||
}
|
||||
@ -174,6 +174,10 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
|
||||
|
||||
export function FactionRoot(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
const player = use.Player();
|
||||
const router = use.Router();
|
||||
const [purchasingAugs, setPurchasingAugs] = useState(props.augPage);
|
||||
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
@ -185,7 +189,16 @@ export function FactionRoot(props: IProps): React.ReactElement {
|
||||
|
||||
const faction = props.faction;
|
||||
|
||||
const [purchasingAugs, setPurchasingAugs] = useState(false);
|
||||
if (player && !player.factions.includes(faction.name)) {
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h4" color="primary">
|
||||
You have not joined {faction.name} yet!
|
||||
</Typography>
|
||||
<Button onClick={() => router.toFactions()}>Back to Factions</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return purchasingAugs ? (
|
||||
<AugmentationsPage faction={faction} routeToMainPage={() => setPurchasingAugs(false)} />
|
||||
|
@ -41,6 +41,10 @@ export function FactionsRoot(props: IProps): React.ReactElement {
|
||||
props.router.toFaction(faction);
|
||||
}
|
||||
|
||||
function openFactionAugPage(faction: Faction): void {
|
||||
props.router.toFaction(faction, true);
|
||||
}
|
||||
|
||||
function acceptInvitation(event: React.MouseEvent<HTMLButtonElement, MouseEvent>, faction: string): void {
|
||||
if (!event.isTrusted) return;
|
||||
joinFaction(Factions[faction]);
|
||||
@ -61,7 +65,7 @@ export function FactionsRoot(props: IProps): React.ReactElement {
|
||||
</Typography>
|
||||
{(props.player.factions.length > 0 && (
|
||||
<Paper sx={{ my: 1, p: 1, pb: 0, display: "inline-block" }}>
|
||||
<Table padding="none">
|
||||
<Table padding="none" style={{ width: "fit-content" }}>
|
||||
<TableBody>
|
||||
{props.player.factions.map((faction: string) => (
|
||||
<TableRow key={faction}>
|
||||
@ -75,6 +79,17 @@ export function FactionsRoot(props: IProps): React.ReactElement {
|
||||
<Button onClick={() => openFaction(Factions[faction])}>Details</Button>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Box ml={1} mb={1}>
|
||||
<Button sx={{ width: '100%' }} onClick={() => openFactionAugPage(Factions[faction])}>
|
||||
Augmentations Left: {Factions[faction]
|
||||
.augmentations
|
||||
.filter((augmentation: string) =>
|
||||
!props.player.hasAugmentation(augmentation))
|
||||
.length}
|
||||
</Button>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
|
@ -4,8 +4,8 @@
|
||||
*/
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { FactionInfo } from "../../Faction/FactionInfo";
|
||||
import { Faction } from "../Faction";
|
||||
import { FactionInfo } from "../FactionInfo";
|
||||
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
import { Favor } from "../../ui/React/Favor";
|
||||
|
@ -9,7 +9,7 @@ import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
|
||||
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { Faction } from "../Faction";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
|
@ -273,7 +273,7 @@ export function EquipmentsSubpage(): React.ReactElement {
|
||||
sx={{ m: 1, width: '15%' }}
|
||||
/>
|
||||
|
||||
<Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr', width: 'fit-content' }}>
|
||||
<Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr', width: '100%' }}>
|
||||
{members.map((member: GangMember) => (
|
||||
<GangMemberUpgradePanel key={member.name} member={member} />
|
||||
))}
|
||||
|
@ -127,10 +127,10 @@ Cities[CityName.NewTokyo].asciiArt = `
|
||||
|
||||
o
|
||||
\\
|
||||
\\ [defcomm]
|
||||
[arcade] E [defcomm]
|
||||
\\
|
||||
o--x---A--x--o [travel agency]
|
||||
7 8 10 G
|
||||
7 8 10 H
|
||||
[vitalife] o 12 [global pharmaceuticals]
|
||||
|
|
||||
o--D-x----x-------x-C-+--------x--x-B-x---x-o
|
||||
@ -141,14 +141,14 @@ Cities[CityName.NewTokyo].asciiArt = `
|
||||
\\
|
||||
[hospital] o 15 [world stock exchange]
|
||||
|
|
||||
o--x--E--x-----x-----x---+---x----x--H--x-o
|
||||
o--x--F--x-----x-----x---+---x----x--I--x-o
|
||||
|
|
||||
|
|
||||
o 17
|
||||
|
||||
|
||||
|
||||
F [the slums]
|
||||
G [the slums]
|
||||
`;
|
||||
Cities[CityName.Sector12].asciiArt = `
|
||||
78 o 97
|
||||
|
@ -54,6 +54,7 @@ export enum LocationName {
|
||||
NewTokyoGlobalPharmaceuticals = "Global Pharmaceuticals",
|
||||
NewTokyoNoodleBar = "Noodle Bar",
|
||||
NewTokyoVitaLife = "VitaLife",
|
||||
NewTokyoArcade = "Arcade",
|
||||
|
||||
// Ishima
|
||||
IshimaNovaMedical = "Nova Medical",
|
||||
|
@ -215,6 +215,11 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
name: LocationName.NewTokyoVitaLife,
|
||||
types: [LocationType.Company, LocationType.Special],
|
||||
},
|
||||
{
|
||||
city: CityName.NewTokyo,
|
||||
name: LocationName.NewTokyoArcade,
|
||||
types: [LocationType.Special],
|
||||
},
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
|
@ -34,10 +34,9 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
padding: "0px",
|
||||
cursor: "pointer",
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
function toLocation(router: IRouter, location: Location): void {
|
||||
if (location.name === LocationName.TravelAgency) {
|
||||
router.toTravel();
|
||||
@ -132,12 +131,14 @@ function ASCIICity(props: IProps): React.ReactElement {
|
||||
|
||||
const elems: JSX.Element[] = [];
|
||||
const lines = props.city.asciiArt.split("\n");
|
||||
let i = 0;
|
||||
for (const line of lines) {
|
||||
elems.push(
|
||||
<Typography key={line} sx={{ lineHeight: "1em", whiteSpace: "pre" }}>
|
||||
<Typography key={i} sx={{ lineHeight: "1em", whiteSpace: "pre" }}>
|
||||
{lineElems(line)}
|
||||
</Typography>,
|
||||
);
|
||||
i++;
|
||||
}
|
||||
|
||||
return <>{elems}</>;
|
||||
|
@ -32,6 +32,7 @@ import { CorruptableText } from "../../ui/React/CorruptableText";
|
||||
import { HacknetNode } from "../../Hacknet/HacknetNode";
|
||||
import { HacknetServer } from "../../Hacknet/HacknetServer";
|
||||
import { GetServer } from "../../Server/AllServers";
|
||||
import { ArcadeRoot } from "../../Arcade/ui/ArcadeRoot";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
@ -81,7 +82,12 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
return <></>;
|
||||
}
|
||||
const text = inBladeburner ? "Enter Bladeburner Headquarters" : "Apply to Bladeburner Division";
|
||||
return <><br/><Button onClick={handleBladeburner}>{text}</Button></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Button onClick={handleBladeburner}>{text}</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function renderNoodleBar(): React.ReactElement {
|
||||
@ -311,6 +317,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
case LocationName.IshimaGlitch: {
|
||||
return renderGlitch();
|
||||
}
|
||||
case LocationName.NewTokyoArcade: {
|
||||
return <ArcadeRoot />;
|
||||
}
|
||||
default:
|
||||
console.error(`Location ${props.loc.name} doesn't have any special properties`);
|
||||
return <></>;
|
||||
|
@ -59,6 +59,8 @@ export const RamCostConstants: IMap<number> = {
|
||||
|
||||
ScriptBladeburnerApiBaseRamCost: 4,
|
||||
|
||||
ScriptStanekWidth: 0.4,
|
||||
ScriptStanekHeight: 0.4,
|
||||
ScriptStanekCharge: 0.4,
|
||||
ScriptStanekFragmentDefinitions: 0,
|
||||
ScriptStanekPlacedFragments: 5,
|
||||
@ -354,6 +356,8 @@ export const RamCosts: IMap<any> = {
|
||||
},
|
||||
|
||||
stanek: {
|
||||
width: RamCostConstants.ScriptStanekWidth,
|
||||
height: RamCostConstants.ScriptStanekHeight,
|
||||
charge: RamCostConstants.ScriptStanekCharge,
|
||||
fragmentDefinitions: RamCostConstants.ScriptStanekFragmentDefinitions,
|
||||
activeFragments: RamCostConstants.ScriptStanekPlacedFragments,
|
||||
|
@ -60,8 +60,5 @@ export function isScriptErrorMessage(msg: string): boolean {
|
||||
return false;
|
||||
}
|
||||
const splitMsg = msg.split("|DELIMITER|");
|
||||
if (splitMsg.length != 4) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return splitMsg.length == 4;
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
throw makeRuntimeRejectMsg(
|
||||
workerScript,
|
||||
`Invalid scriptArgs argument passed into getRunningScript() from ${callingFnName}(). ` +
|
||||
`This is probably a bug. Please report to game developer`,
|
||||
`This is probably a bug. Please report to game developer`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -692,8 +692,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
workerScript.log(
|
||||
"weaken",
|
||||
() =>
|
||||
`'${server.hostname}' security level weakened to ${
|
||||
server.hackDifficulty
|
||||
`'${server.hostname}' security level weakened to ${server.hackDifficulty
|
||||
}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)})`,
|
||||
);
|
||||
workerScript.scriptRef.onlineExpGained += expGain;
|
||||
@ -807,7 +806,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
if (possibleLogs[fn] === undefined) {
|
||||
throw makeRuntimeErrorMsg("isLogEnabled", `Invalid argument: ${fn}.`);
|
||||
}
|
||||
return workerScript.disableLogs[fn] ? false : true;
|
||||
return !workerScript.disableLogs[fn];
|
||||
},
|
||||
getScriptLogs: function (fn: any, hostname: any, ...scriptArgs: any): any {
|
||||
const runningScriptObj = getRunningScript(fn, hostname, "getScriptLogs", scriptArgs);
|
||||
@ -1590,10 +1589,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
}
|
||||
}
|
||||
const txtFile = getTextFile(filename, server);
|
||||
if (txtFile != null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return txtFile != null;
|
||||
},
|
||||
isRunning: function (fn: any, hostname: any = workerScript.hostname, ...scriptArgs: any): any {
|
||||
updateDynamicRam("isRunning", getRamCost(Player, "isRunning"));
|
||||
@ -1628,6 +1624,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
return cost;
|
||||
},
|
||||
purchaseServer: function (aname: any, aram: any): any {
|
||||
if (arguments.length !== 2) throw makeRuntimeErrorMsg("purchaseServer", "Takes 2 arguments");
|
||||
const name = helper.string("purchaseServer", "name", aname);
|
||||
const ram = helper.number("purchaseServer", "ram", aram);
|
||||
updateDynamicRam("purchaseServer", getRamCost(Player, "purchaseServer"));
|
||||
@ -1649,12 +1646,12 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
|
||||
const cost = getPurchaseServerCost(ram);
|
||||
if (cost === Infinity) {
|
||||
if(ram > getPurchaseServerMaxRam()){
|
||||
if (ram > getPurchaseServerMaxRam()) {
|
||||
workerScript.log("purchaseServer", () => `Invalid argument: ram='${ram}' must not be greater than getPurchaseServerMaxRam`);
|
||||
}else{
|
||||
} else {
|
||||
workerScript.log("purchaseServer", () => `Invalid argument: ram='${ram}' must be a positive power of 2`);
|
||||
}
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -1836,6 +1833,12 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
},
|
||||
tryWritePort: function (port: any, data: any = ""): any {
|
||||
updateDynamicRam("tryWritePort", getRamCost(Player, "tryWritePort"));
|
||||
if (typeof data !== "string" && typeof data !== "number") {
|
||||
throw makeRuntimeErrorMsg(
|
||||
"tryWritePort",
|
||||
`Trying to write invalid data to a port: only strings and numbers are valid.`,
|
||||
);
|
||||
}
|
||||
if (!isNaN(port)) {
|
||||
port = Math.round(port);
|
||||
if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
|
||||
|
@ -414,8 +414,8 @@ export function NetscriptCorporation(
|
||||
const cityName = helper.string("sellProduct", "cityName", acityName);
|
||||
const enabled = helper.boolean(aenabled);
|
||||
const warehouse = getWarehouse(divisionName, cityName);
|
||||
if (!hasUnlockUpgrade("SmartSupply"))
|
||||
throw helper.makeRuntimeErrorMsg(`corporation.setSmartSupply`, `You have not purchased the SmartSupply upgrade!`);
|
||||
if (!hasUnlockUpgrade("Smart Supply"))
|
||||
throw helper.makeRuntimeErrorMsg(`corporation.setSmartSupply`, `You have not purchased the Smart Supply upgrade!`);
|
||||
SetSmartSupply(warehouse, enabled);
|
||||
},
|
||||
setSmartSupplyUseLeftovers: function (adivisionName: any, acityName: any, amaterialName: any, aenabled: any): void {
|
||||
@ -426,8 +426,8 @@ export function NetscriptCorporation(
|
||||
const enabled = helper.boolean(aenabled);
|
||||
const warehouse = getWarehouse(divisionName, cityName);
|
||||
const material = getMaterial(divisionName, cityName, materialName);
|
||||
if (!hasUnlockUpgrade("SmartSupply"))
|
||||
throw helper.makeRuntimeErrorMsg(`corporation.setSmartSupply`, `You have not purchased the SmartSupply upgrade!`);
|
||||
if (!hasUnlockUpgrade("Smart Supply"))
|
||||
throw helper.makeRuntimeErrorMsg(`corporation.setSmartSupply`, `You have not purchased the Smart Supply upgrade!`);
|
||||
SetSmartSupplyUseLeftovers(warehouse, material, enabled);
|
||||
},
|
||||
buyMaterial: function (adivisionName: any, acityName: any, amaterialName: any, aamt: any): void {
|
||||
|
@ -23,9 +23,13 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, hel
|
||||
|
||||
return {
|
||||
width: function (): number {
|
||||
helper.updateDynamicRam("width", getRamCost(player, "stanek", "width"));
|
||||
checkStanekAPIAccess("width");
|
||||
return staneksGift.width();
|
||||
},
|
||||
height: function (): number {
|
||||
helper.updateDynamicRam("height", getRamCost(player, "stanek", "height"));
|
||||
checkStanekAPIAccess("height");
|
||||
return staneksGift.height();
|
||||
},
|
||||
charge: function (arootX: unknown, arootY: unknown): Promise<void> {
|
||||
|
@ -25,7 +25,7 @@ import { Cities } from "../../Locations/Cities";
|
||||
import { Locations } from "../../Locations/Locations";
|
||||
import { CityName } from "../../Locations/data/CityNames";
|
||||
import { LocationName } from "../../Locations/data/LocationNames";
|
||||
import { Sleeve } from "../../PersonObjects/Sleeve/Sleeve";
|
||||
import { Sleeve } from "../Sleeve/Sleeve";
|
||||
import {
|
||||
calculateSkill as calculateSkillF,
|
||||
calculateSkillProgress as calculateSkillProgressF,
|
||||
@ -590,7 +590,7 @@ export function process(this: IPlayer, router: IRouter, numCycles = 1): void {
|
||||
if (this.isWorking) {
|
||||
if (this.workType == CONSTANTS.WorkTypeFaction) {
|
||||
if (this.workForFaction(numCycles)) {
|
||||
router.toFaction();
|
||||
router.toFaction(Factions[this.currentWorkFactionName]);
|
||||
}
|
||||
} else if (this.workType == CONSTANTS.WorkTypeCreateProgram) {
|
||||
if (this.createProgramWork(numCycles)) {
|
||||
@ -933,6 +933,8 @@ export function startFactionSecurityWork(this: IPlayer, faction: Faction): void
|
||||
export function workForFaction(this: IPlayer, numCycles: number): boolean {
|
||||
const faction = Factions[this.currentWorkFactionName];
|
||||
|
||||
if (!faction) { return false; }
|
||||
|
||||
//Constantly update the rep gain rate
|
||||
switch (this.factionWorkType) {
|
||||
case CONSTANTS.FactionWorkHacking:
|
||||
@ -2002,18 +2004,13 @@ export function isQualified(this: IPlayer, company: Company, position: CompanyPo
|
||||
const reqAgility = position.requiredDexterity > 0 ? position.requiredDexterity + offset : 0;
|
||||
const reqCharisma = position.requiredCharisma > 0 ? position.requiredCharisma + offset : 0;
|
||||
|
||||
if (
|
||||
this.hacking >= reqHacking &&
|
||||
return this.hacking >= reqHacking &&
|
||||
this.strength >= reqStrength &&
|
||||
this.defense >= reqDefense &&
|
||||
this.dexterity >= reqDexterity &&
|
||||
this.agility >= reqAgility &&
|
||||
this.charisma >= reqCharisma &&
|
||||
company.playerReputation >= position.requiredReputation
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
company.playerReputation >= position.requiredReputation;
|
||||
}
|
||||
|
||||
/********** Reapplying Augmentations and Source File ***********/
|
||||
@ -2571,7 +2568,7 @@ export function queueAugmentation(this: IPlayer, name: string): void {
|
||||
|
||||
/************* Coding Contracts **************/
|
||||
export function gainCodingContractReward(this: IPlayer, reward: ICodingContractReward, difficulty = 1): string {
|
||||
if (reward == null || reward.type == null || reward == null) {
|
||||
if (reward == null || reward.type == null) {
|
||||
return `No reward for this contract`;
|
||||
}
|
||||
|
||||
|
@ -453,11 +453,11 @@ export class Sleeve extends Person {
|
||||
// Reset augs and multipliers
|
||||
this.augmentations = [];
|
||||
this.resetMultipliers();
|
||||
|
||||
|
||||
// Reset Location
|
||||
|
||||
this.city=CityName.Sector12;
|
||||
|
||||
|
||||
this.city = CityName.Sector12;
|
||||
|
||||
// Reset sleeve-related stats
|
||||
this.shock = 1;
|
||||
this.storedCycles = 0;
|
||||
@ -859,10 +859,8 @@ export class Sleeve extends Person {
|
||||
* Returns boolean indicating success
|
||||
*/
|
||||
workForFaction(p: IPlayer, factionName: string, workType: string): boolean {
|
||||
if (factionName === "") {
|
||||
return false;
|
||||
}
|
||||
if (!(Factions[factionName] instanceof Faction) || !p.factions.includes(factionName)) {
|
||||
const faction = Factions[factionName]
|
||||
if (factionName === "" || !faction || !(faction instanceof Faction) || !p.factions.includes(factionName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -872,7 +870,7 @@ export class Sleeve extends Person {
|
||||
this.resetTaskStatus();
|
||||
}
|
||||
|
||||
const factionInfo = Factions[factionName].getInfo();
|
||||
const factionInfo = faction.getInfo();
|
||||
|
||||
// Set type of work (hacking/field/security), and the experience gains
|
||||
const sanitizedWorkType: string = workType.toLowerCase();
|
||||
|
@ -17,10 +17,10 @@ import { Money } from "../../../ui/React/Money";
|
||||
import { MoneyRate } from "../../../ui/React/MoneyRate";
|
||||
import { use } from "../../../ui/Context";
|
||||
import { ReputationRate } from "../../../ui/React/ReputationRate";
|
||||
import { StatsElement } from "../ui/StatsElement";
|
||||
import { StatsElement } from "./StatsElement";
|
||||
import { MoreStatsModal } from "./MoreStatsModal";
|
||||
import { MoreEarningsModal } from "../ui/MoreEarningsModal";
|
||||
import { TaskSelector } from "../ui/TaskSelector";
|
||||
import { MoreEarningsModal } from "./MoreEarningsModal";
|
||||
import { TaskSelector } from "./TaskSelector";
|
||||
import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum";
|
||||
import { StatsTable } from "../../../ui/React/StatsTable";
|
||||
|
||||
|
@ -75,7 +75,10 @@ function possibleFactions(player: IPlayer, sleeve: Sleeve): string[] {
|
||||
}
|
||||
}
|
||||
|
||||
return factions;
|
||||
return factions.filter(faction => {
|
||||
const facInfo = Factions[faction].getInfo();
|
||||
return facInfo.offerHackingWork || facInfo.offerFieldWork || facInfo.offerSecurityWork
|
||||
});
|
||||
}
|
||||
|
||||
const tasks: {
|
||||
|
@ -161,7 +161,7 @@ class BitburnerSaveObject {
|
||||
return reject(new Error("Error importing file"));
|
||||
}
|
||||
const result = target.result;
|
||||
if (typeof result !== "string" || result === null) {
|
||||
if (typeof result !== "string") {
|
||||
return reject(new Error("FileReader event was not type string"));
|
||||
}
|
||||
const contents = result;
|
||||
|
@ -11,7 +11,7 @@ import acorn, { parse } from "acorn";
|
||||
import { RamCalculationErrorCode } from "./RamCalculationErrorCodes";
|
||||
|
||||
import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator";
|
||||
import { Script } from "../Script/Script";
|
||||
import { Script } from "./Script";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { areImportsEquals } from "../Terminal/DirectoryHelpers";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
@ -2,7 +2,7 @@ import { CONSTANTS } from "../Constants";
|
||||
import { Player } from "../Player";
|
||||
import { BaseServer } from "../Server/BaseServer";
|
||||
import { Server } from "../Server/Server";
|
||||
import { RunningScript } from "../Script/RunningScript";
|
||||
import { RunningScript } from "./RunningScript";
|
||||
import { processSingleServerGrowth } from "../Server/ServerHelpers";
|
||||
import { GetServer } from "../Server/AllServers";
|
||||
|
||||
|
19
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
19
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -535,6 +535,8 @@ export interface BitNodeMultipliers {
|
||||
CompanyWorkExpGain: number;
|
||||
/** Influences how much money the player earns when completing working their job. */
|
||||
CompanyWorkMoney: number;
|
||||
/** Influences the money gain from dividends of corporations created by the player. */
|
||||
CorporationSoftCap: number;
|
||||
/** Influences the valuation of corporations created by the player. */
|
||||
CorporationValuation: number;
|
||||
/** Influences the base experience gained for each ability when the player commits a crime. */
|
||||
@ -903,9 +905,9 @@ export interface GangMemberInfo {
|
||||
/** Name of the gang member */
|
||||
name: string;
|
||||
/** Currently assigned task */
|
||||
task: string;
|
||||
task: string;
|
||||
earnedRespect: number;
|
||||
|
||||
|
||||
/** Hack skill level */
|
||||
hack: number;
|
||||
/** Strength skill level */
|
||||
@ -5650,7 +5652,7 @@ export interface NS extends Singularity {
|
||||
* @param data - Data to write.
|
||||
* @returns True if the data is successfully written to the port, and false otherwise.
|
||||
*/
|
||||
tryWritePort(port: number, data: string[] | number): Promise<boolean>;
|
||||
tryWritePort(port: number, data: string | number): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Read content of a file.
|
||||
@ -6902,3 +6904,14 @@ interface GameInfo {
|
||||
commit: string;
|
||||
platform: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for autocompletion
|
||||
* @public
|
||||
*/
|
||||
interface AutocompleteData {
|
||||
servers: string[];
|
||||
scripts: string[];
|
||||
txts: string[];
|
||||
flags(schema: [string, string | number | boolean | string[]][]): any;
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ import Typography from "@mui/material/Typography";
|
||||
import Link from "@mui/material/Link";
|
||||
import Box from "@mui/material/Box";
|
||||
import SettingsIcon from "@mui/icons-material/Settings";
|
||||
import SyncIcon from '@mui/icons-material/Sync';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import SyncIcon from "@mui/icons-material/Sync";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import Table from "@mui/material/Table";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
@ -133,13 +133,12 @@ export function Root(props: IProps): React.ReactElement {
|
||||
// Prevent Crash if script is open on deleted server
|
||||
openScripts = openScripts.filter((script) => {
|
||||
return GetServer(script.hostname) !== null;
|
||||
})
|
||||
if (currentScript && (GetServer(currentScript.hostname) === null)) {
|
||||
});
|
||||
if (currentScript && GetServer(currentScript.hostname) === null) {
|
||||
currentScript = openScripts[0];
|
||||
if (currentScript === undefined) currentScript = null;
|
||||
}
|
||||
|
||||
|
||||
const [dimensions, setDimensions] = useState({
|
||||
height: window.innerHeight,
|
||||
width: window.innerWidth,
|
||||
@ -205,9 +204,35 @@ export function Root(props: IProps): React.ReactElement {
|
||||
save();
|
||||
props.router.toTerminal();
|
||||
});
|
||||
|
||||
// Setup "go to next tab" and "go to previous tab". This is a little more involved
|
||||
// since these aren't Ex commands (they run in normal mode, not after typing `:`)
|
||||
MonacoVim.VimMode.Vim.defineAction("nextTabs", function (_cm: any, args: { repeat?: number }) {
|
||||
const nTabs = args.repeat ?? 1;
|
||||
// Go to the next tab (to the right). Wraps around when at the rightmost tab
|
||||
const currIndex = currentTabIndex();
|
||||
if (currIndex !== undefined) {
|
||||
const nextIndex = (currIndex + nTabs) % openScripts.length;
|
||||
onTabClick(nextIndex);
|
||||
}
|
||||
});
|
||||
MonacoVim.VimMode.Vim.defineAction("prevTabs", function (_cm: any, args: { repeat?: number }) {
|
||||
const nTabs = args.repeat ?? 1;
|
||||
// Go to the previous tab (to the left). Wraps around when at the leftmost tab
|
||||
const currIndex = currentTabIndex();
|
||||
if (currIndex !== undefined) {
|
||||
let nextIndex = currIndex - nTabs;
|
||||
while (nextIndex < 0) {
|
||||
nextIndex += openScripts.length;
|
||||
}
|
||||
onTabClick(nextIndex);
|
||||
}
|
||||
});
|
||||
MonacoVim.VimMode.Vim.mapCommand("gt", "action", "nextTabs", {}, { context: "normal" });
|
||||
MonacoVim.VimMode.Vim.mapCommand("gT", "action", "prevTabs", {}, { context: "normal" });
|
||||
editor.focus();
|
||||
});
|
||||
} catch { }
|
||||
} catch {}
|
||||
} else if (!options.vim) {
|
||||
// Whem vim mode is disabled
|
||||
vimEditor?.dispose();
|
||||
@ -316,13 +341,18 @@ export function Root(props: IProps): React.ReactElement {
|
||||
.loader();
|
||||
// replaced the bare tokens with regexes surrounded by \b, e.g. \b{token}\b which matches a word-break on either side
|
||||
// this prevents the highlighter from highlighting pieces of variables that start with a reserved token name
|
||||
l.language.tokenizer.root.unshift([new RegExp('\\bns\\b'), { token: "ns" }]);
|
||||
for (const symbol of symbols) l.language.tokenizer.root.unshift([new RegExp(`\\b${symbol}\\b`), { token: "netscriptfunction" }]);
|
||||
l.language.tokenizer.root.unshift([new RegExp("\\bns\\b"), { token: "ns" }]);
|
||||
for (const symbol of symbols)
|
||||
l.language.tokenizer.root.unshift([new RegExp(`\\b${symbol}\\b`), { token: "netscriptfunction" }]);
|
||||
const otherKeywords = ["let", "const", "var", "function"];
|
||||
const otherKeyvars = ["true", "false", "null", "undefined"];
|
||||
otherKeywords.forEach((k) => l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeywords" }]));
|
||||
otherKeyvars.forEach((k) => l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeyvars" }]));
|
||||
l.language.tokenizer.root.unshift([new RegExp('\\bthis\\b'), { token: "this" }]);
|
||||
otherKeywords.forEach((k) =>
|
||||
l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeywords" }]),
|
||||
);
|
||||
otherKeyvars.forEach((k) =>
|
||||
l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeyvars" }]),
|
||||
);
|
||||
l.language.tokenizer.root.unshift([new RegExp("\\bthis\\b"), { token: "this" }]);
|
||||
})();
|
||||
|
||||
const source = (libSource + "").replace(/export /g, "");
|
||||
@ -448,7 +478,7 @@ export function Root(props: IProps): React.ReactElement {
|
||||
}
|
||||
try {
|
||||
infLoop(newCode);
|
||||
} catch (err) { }
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
function saveScript(scriptToSave: OpenScript): void {
|
||||
@ -604,16 +634,26 @@ export function Root(props: IProps): React.ReactElement {
|
||||
openScripts = items;
|
||||
}
|
||||
|
||||
function onTabClick(index: number): void {
|
||||
function currentTabIndex(): number | undefined {
|
||||
if (currentScript !== null) {
|
||||
// Save currentScript to openScripts
|
||||
const curIndex = openScripts.findIndex(
|
||||
return openScripts.findIndex(
|
||||
(script) =>
|
||||
currentScript !== null &&
|
||||
script.fileName === currentScript.fileName &&
|
||||
script.hostname === currentScript.hostname,
|
||||
);
|
||||
openScripts[curIndex] = currentScript;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function onTabClick(index: number): void {
|
||||
if (currentScript !== null) {
|
||||
// Save currentScript to openScripts
|
||||
const curIndex = currentTabIndex();
|
||||
if (curIndex !== undefined) {
|
||||
openScripts[curIndex] = currentScript;
|
||||
}
|
||||
}
|
||||
|
||||
currentScript = { ...openScripts[index] };
|
||||
@ -701,15 +741,17 @@ export function Root(props: IProps): React.ReactElement {
|
||||
|
||||
if (openScript.code !== serverScriptCode) {
|
||||
PromptEvent.emit({
|
||||
txt: "Do you want to overwrite the current editor content with the contents of " +
|
||||
openScript.fileName + " on the server? This cannot be undone.",
|
||||
txt:
|
||||
"Do you want to overwrite the current editor content with the contents of " +
|
||||
openScript.fileName +
|
||||
" on the server? This cannot be undone.",
|
||||
resolve: (result: boolean) => {
|
||||
if (result) {
|
||||
// Save changes
|
||||
openScript.code = serverScriptCode;
|
||||
|
||||
// Switch to target tab
|
||||
onTabClick(index)
|
||||
onTabClick(index);
|
||||
|
||||
if (editorRef.current !== null && openScript !== null) {
|
||||
if (openScript.model === undefined || openScript.model.isDisposed()) {
|
||||
@ -753,11 +795,11 @@ export function Root(props: IProps): React.ReactElement {
|
||||
// 44px bottom tool bar + 16px margin
|
||||
// + vim bar 34px
|
||||
const editorHeight = dimensions.height - (130 + (options.vim ? 34 : 0));
|
||||
const tabsMaxWidth = 1640
|
||||
const tabMargin = 5
|
||||
const tabMaxWidth = openScripts.length ? (tabsMaxWidth / openScripts.length) - tabMargin : 0
|
||||
const tabIconWidth = 25
|
||||
const tabTextWidth = tabMaxWidth - (tabIconWidth * 2)
|
||||
const tabsMaxWidth = 1640;
|
||||
const tabMargin = 5;
|
||||
const tabMaxWidth = openScripts.length ? tabsMaxWidth / openScripts.length - tabMargin : 0;
|
||||
const tabIconWidth = 25;
|
||||
const tabTextWidth = tabMaxWidth - tabIconWidth * 2;
|
||||
return (
|
||||
<>
|
||||
<div style={{ display: currentScript !== null ? "block" : "none", height: "100%", width: "100%" }}>
|
||||
@ -783,20 +825,22 @@ export function Root(props: IProps): React.ReactElement {
|
||||
const iconButtonStyle = {
|
||||
maxWidth: `${tabIconWidth}px`,
|
||||
minWidth: `${tabIconWidth}px`,
|
||||
minHeight: '38.5px',
|
||||
maxHeight: '38.5px',
|
||||
...(currentScript?.fileName === openScripts[index].fileName ? {
|
||||
background: Settings.theme.button,
|
||||
borderColor: Settings.theme.button,
|
||||
color: Settings.theme.primary
|
||||
} : {
|
||||
background: Settings.theme.backgroundsecondary,
|
||||
borderColor: Settings.theme.backgroundsecondary,
|
||||
color: Settings.theme.secondary
|
||||
})
|
||||
minHeight: "38.5px",
|
||||
maxHeight: "38.5px",
|
||||
...(currentScript?.fileName === openScripts[index].fileName
|
||||
? {
|
||||
background: Settings.theme.button,
|
||||
borderColor: Settings.theme.button,
|
||||
color: Settings.theme.primary,
|
||||
}
|
||||
: {
|
||||
background: Settings.theme.backgroundsecondary,
|
||||
borderColor: Settings.theme.backgroundsecondary,
|
||||
color: Settings.theme.secondary,
|
||||
}),
|
||||
};
|
||||
|
||||
const scriptTabText = `${hostname}:~/${fileName} ${dirty(index)}`
|
||||
const scriptTabText = `${hostname}:~/${fileName} ${dirty(index)}`;
|
||||
return (
|
||||
<Draggable
|
||||
key={fileName + hostname}
|
||||
@ -811,51 +855,52 @@ export function Root(props: IProps): React.ReactElement {
|
||||
{...provided.dragHandleProps}
|
||||
style={{
|
||||
...provided.draggableProps.style,
|
||||
minWidth: '200px',
|
||||
maxWidth: `${tabMaxWidth}px`,
|
||||
marginRight: `${tabMargin}px`,
|
||||
flexShrink: 0,
|
||||
border: '1px solid ' + Settings.theme.well,
|
||||
border: "1px solid " + Settings.theme.well,
|
||||
}}
|
||||
>
|
||||
<Tooltip title={scriptTabText}>
|
||||
<Button
|
||||
onClick={() => onTabClick(index)}
|
||||
onMouseDown={e => {
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
if (e.button === 1) onTabClose(index);
|
||||
}}
|
||||
style={{
|
||||
maxWidth: `${tabTextWidth}px`,
|
||||
overflow: "hidden",
|
||||
...(currentScript?.fileName === openScripts[index].fileName ? {
|
||||
background: Settings.theme.button,
|
||||
borderColor: Settings.theme.button,
|
||||
color: Settings.theme.primary
|
||||
} : {
|
||||
background: Settings.theme.backgroundsecondary,
|
||||
borderColor: Settings.theme.backgroundsecondary,
|
||||
color: Settings.theme.secondary
|
||||
})
|
||||
...(currentScript?.fileName === openScripts[index].fileName
|
||||
? {
|
||||
background: Settings.theme.button,
|
||||
borderColor: Settings.theme.button,
|
||||
color: Settings.theme.primary,
|
||||
}
|
||||
: {
|
||||
background: Settings.theme.backgroundsecondary,
|
||||
borderColor: Settings.theme.backgroundsecondary,
|
||||
color: Settings.theme.secondary,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<span style={{ overflow: 'hidden', direction: 'rtl', textOverflow: "ellipsis" }}>
|
||||
<span style={{ overflow: "hidden", direction: "rtl", textOverflow: "ellipsis" }}>
|
||||
{scriptTabText}
|
||||
</span>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title="Overwrite editor content with saved file content">
|
||||
<Button onClick={() => onTabUpdate(index)} style={iconButtonStyle} >
|
||||
<SyncIcon fontSize='small' />
|
||||
<Button onClick={() => onTabUpdate(index)} style={iconButtonStyle}>
|
||||
<SyncIcon fontSize="small" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button onClick={() => onTabClose(index)} style={iconButtonStyle}>
|
||||
<CloseIcon fontSize='small' />
|
||||
<CloseIcon fontSize="small" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
)
|
||||
);
|
||||
})}
|
||||
{provided.placeholder}
|
||||
</Box>
|
||||
@ -885,14 +930,24 @@ export function Root(props: IProps): React.ReactElement {
|
||||
></Box>
|
||||
|
||||
<Box display="flex" flexDirection="row" sx={{ m: 1 }} alignItems="center">
|
||||
<Button startIcon={<SettingsIcon />} onClick={() => setOptionsOpen(true)} sx={{ mr: 1 }}>Options</Button>
|
||||
<Button startIcon={<SettingsIcon />} onClick={() => setOptionsOpen(true)} sx={{ mr: 1 }}>
|
||||
Options
|
||||
</Button>
|
||||
<Button onClick={beautify}>Beautify</Button>
|
||||
<Button color={updatingRam ? "secondary" : "primary"} sx={{ mx: 1 }} onClick={() => { setRamInfoOpen(true) }}>
|
||||
<Button
|
||||
color={updatingRam ? "secondary" : "primary"}
|
||||
sx={{ mx: 1 }}
|
||||
onClick={() => {
|
||||
setRamInfoOpen(true);
|
||||
}}
|
||||
>
|
||||
{ram}
|
||||
</Button>
|
||||
<Button onClick={save}>Save (Ctrl/Cmd + s)</Button>
|
||||
<Button onClick={props.router.toTerminal}>Close (Ctrl/Cmd + b)</Button>
|
||||
<Typography sx={{ mx: 1 }}>
|
||||
<Button sx={{ mx: 1 }} onClick={props.router.toTerminal}>
|
||||
Terminal (Ctrl/Cmd + b)
|
||||
</Button>
|
||||
<Typography>
|
||||
{" "}
|
||||
Documentation:{" "}
|
||||
<Link target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">
|
||||
@ -930,7 +985,9 @@ export function Root(props: IProps): React.ReactElement {
|
||||
<React.Fragment key={n + r}>
|
||||
<TableRow>
|
||||
<TableCell sx={{ color: Settings.theme.primary }}>{n}</TableCell>
|
||||
<TableCell align="right" sx={{ color: Settings.theme.primary }}>{r}</TableCell>
|
||||
<TableCell align="right" sx={{ color: Settings.theme.primary }}>
|
||||
{r}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
@ -260,7 +260,7 @@ export async function loadThemes(monaco: { editor: any }): Promise<void> {
|
||||
token: "ns",
|
||||
foreground: "FFB86C",
|
||||
fontStyle: "italic",
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
token: "netscriptfunction",
|
||||
|
@ -14,7 +14,7 @@ import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { IMap } from "../types";
|
||||
import { EventEmitter } from "../utils/EventEmitter";
|
||||
|
||||
import { numeralWrapper } from ".././ui/numeralFormat";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { Reviver } from "../utils/JSONReviver";
|
||||
|
@ -17,7 +17,7 @@ export function expr(
|
||||
const expr = args.join("");
|
||||
|
||||
// Sanitize the math expression
|
||||
const sanitizedExpr = expr.replace(/s+/g, "").replace(/[^-()\d/*+.]/g, "");
|
||||
const sanitizedExpr = expr.replace(/s+/g, "").replace(/[^-()\d/*+.%]/g, "");
|
||||
let result;
|
||||
try {
|
||||
result = eval(sanitizedExpr);
|
||||
|
@ -5,7 +5,7 @@ import { toString } from "lodash";
|
||||
import React from "react";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { BaseServer } from "../../Server/BaseServer";
|
||||
import { evaluateDirectoryPath, getFirstParentDirectory, isValidDirectoryPath } from "../../Terminal/DirectoryHelpers";
|
||||
import { evaluateDirectoryPath, getFirstParentDirectory, isValidDirectoryPath } from "../DirectoryHelpers";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { ITerminal } from "../ITerminal";
|
||||
|
||||
|
@ -36,7 +36,7 @@ export function ps(
|
||||
}
|
||||
terminal.print(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(args.length === 0){
|
||||
for (let i = 0; i < server.runningScripts.length; i++) {
|
||||
const rsObj = server.runningScripts[i];
|
||||
|
@ -10,6 +10,7 @@ import { HelpTexts } from "./HelpText";
|
||||
import { isScriptFilename } from "../Script/isScriptFilename";
|
||||
import { compile } from "../NetscriptJSEvaluator";
|
||||
import { Flags } from "../NetscriptFunctions/Flags";
|
||||
import { AutocompleteData } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import * as libarg from "arg";
|
||||
|
||||
// An array of all Terminal commands
|
||||
@ -298,26 +299,27 @@ export async function determineAllPossibilitiesForTabCompletion(
|
||||
argv: command.slice(2),
|
||||
});
|
||||
const flagFunc = Flags(flags._);
|
||||
const autocompleteData: AutocompleteData = {
|
||||
servers: GetAllServers().map((server) => server.hostname),
|
||||
scripts: currServ.scripts.map((script) => script.filename),
|
||||
txts: currServ.textFiles.map((txt) => txt.fn),
|
||||
flags: (schema: any) => {
|
||||
pos2 = schema.map((f: any) => {
|
||||
if (f[0].length === 1) return "-" + f[0];
|
||||
return "--" + f[0];
|
||||
});
|
||||
try {
|
||||
return flagFunc(schema);
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
}
|
||||
let pos: string[] = [];
|
||||
let pos2: string[] = [];
|
||||
pos = pos.concat(
|
||||
loadedModule.autocomplete(
|
||||
{
|
||||
servers: GetAllServers().map((server) => server.hostname),
|
||||
scripts: currServ.scripts.map((script) => script.filename),
|
||||
txts: currServ.textFiles.map((txt) => txt.fn),
|
||||
flags: (schema: any) => {
|
||||
pos2 = schema.map((f: any) => {
|
||||
if (f[0].length === 1) return "-" + f[0];
|
||||
return "--" + f[0];
|
||||
});
|
||||
try {
|
||||
return flagFunc(schema);
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
},
|
||||
autocompleteData,
|
||||
flags._,
|
||||
),
|
||||
);
|
||||
|
@ -356,12 +356,12 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
||||
event.preventDefault();
|
||||
modifyInput("deletewordbefore");
|
||||
}
|
||||
|
||||
|
||||
if (event.keyCode === KEY.D && event.altKey) {
|
||||
event.preventDefault();
|
||||
modifyInput("deletewordafter");
|
||||
}
|
||||
|
||||
|
||||
if (event.keyCode === KEY.U && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
modifyInput("clearbefore");
|
||||
|
@ -307,15 +307,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
reach = Math.max(i + data[i], reach);
|
||||
}
|
||||
const solution: boolean = i === n;
|
||||
|
||||
if (ans === "1" && solution) {
|
||||
return true;
|
||||
}
|
||||
if (ans === "0" && !solution) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return (ans === "1" && solution) || (ans === "0" && !solution);
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -921,7 +913,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
"The provided answer should be an array of strings containing the valid expressions.",
|
||||
"The data provided by this problem is an array with two elements. The first element",
|
||||
"is the string of digits, while the second element is the target number:\n\n",
|
||||
`["${digits}", ${target}]\n\n`,
|
||||
`["${digits}", ${target}]\n\n`,
|
||||
"NOTE: The order of evaluation expects script operator precedence",
|
||||
"NOTE: Numbers in the expression cannot have leading 0's. In other words,",
|
||||
`"1+01" is not a valid expression`,
|
||||
|
@ -401,9 +401,12 @@ const Engine: {
|
||||
() =>
|
||||
AlertEvents.emit(
|
||||
<>
|
||||
Offline for {timeOfflineString}. While you were offline, your scripts generated{" "}
|
||||
<Money money={offlineHackingIncome} />, your Hacknet Nodes generated {hacknetProdInfo} and you gained{" "}
|
||||
<Reputation reputation={offlineReputation} /> reputation divided amongst your factions.
|
||||
Offline for {timeOfflineString}. While you were offline:
|
||||
<ul>
|
||||
<li>Your scripts generated{" "} <Money money={offlineHackingIncome} /></li>
|
||||
<li>Your Hacknet Nodes generated {hacknetProdInfo}</li>
|
||||
<li>You gained{" "} <Reputation reputation={offlineReputation} /> reputation divided amongst your factions</li>
|
||||
</ul>
|
||||
</>,
|
||||
),
|
||||
250,
|
||||
|
@ -26,7 +26,7 @@ import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFuncti
|
||||
import { arrayToString } from "../../utils/helpers/arrayToString";
|
||||
import { Money } from "../React/Money";
|
||||
import { MoneyRate } from "../React/MoneyRate";
|
||||
import { RecentScript } from "../..//Netscript/RecentScripts";
|
||||
import { RecentScript } from "../../Netscript/RecentScripts";
|
||||
import { LogBoxEvents } from "../React/LogBoxManager";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { numeralWrapper } from "./numeralFormat";
|
||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
|
@ -44,7 +44,7 @@ import { CorporationRoot } from "../Corporation/ui/CorporationRoot";
|
||||
import { InfiltrationRoot } from "../Infiltration/ui/InfiltrationRoot";
|
||||
import { ResleeveRoot } from "../PersonObjects/Resleeving/ui/ResleeveRoot";
|
||||
import { WorkInProgressRoot } from "./WorkInProgressRoot";
|
||||
import { GameOptionsRoot } from "../ui/React/GameOptionsRoot";
|
||||
import { GameOptionsRoot } from "./React/GameOptionsRoot";
|
||||
import { SleeveRoot } from "../PersonObjects/Sleeve/ui/SleeveRoot";
|
||||
import { HacknetRoot } from "../Hacknet/ui/HacknetRoot";
|
||||
import { GenericLocation } from "../Locations/ui/GenericLocation";
|
||||
@ -54,7 +54,7 @@ import { Root as ScriptEditorRoot } from "../ScriptEditor/ui/ScriptEditorRoot";
|
||||
import { MilestonesRoot } from "../Milestones/ui/MilestonesRoot";
|
||||
import { TerminalRoot } from "../Terminal/ui/TerminalRoot";
|
||||
import { TutorialRoot } from "../Tutorial/ui/TutorialRoot";
|
||||
import { ActiveScriptsRoot } from "../ui/ActiveScripts/ActiveScriptsRoot";
|
||||
import { ActiveScriptsRoot } from "./ActiveScripts/ActiveScriptsRoot";
|
||||
import { FactionsRoot } from "../Faction/ui/FactionsRoot";
|
||||
import { FactionRoot } from "../Faction/ui/FactionRoot";
|
||||
import { CharacterStats } from "./CharacterStats";
|
||||
@ -110,106 +110,45 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
}),
|
||||
);
|
||||
|
||||
const uninitialized = (): any => {
|
||||
throw new Error("Router called before initialization");
|
||||
};
|
||||
|
||||
export let Router: IRouter = {
|
||||
page: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
allowRouting: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toActiveScripts: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toAugmentations: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toBitVerse: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toBladeburner: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toStats: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toCity: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toCorporation: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toCreateProgram: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toDevMenu: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toFaction: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toFactions: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toGameOptions: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toGang: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toHacknetNodes: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toInfiltration: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toJob: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toMilestones: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toResleeves: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toScriptEditor: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toSleeves: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toStockMarket: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toTerminal: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toTravel: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toTutorial: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toWork: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toBladeburnerCinematic: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toLocation: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toStaneksGift: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toAchievements: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toThemeBrowser: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toImportSave: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
isInitialized: false,
|
||||
page: uninitialized,
|
||||
allowRouting: uninitialized,
|
||||
toActiveScripts: uninitialized,
|
||||
toAugmentations: uninitialized,
|
||||
toBitVerse: uninitialized,
|
||||
toBladeburner: uninitialized,
|
||||
toStats: uninitialized,
|
||||
toCity: uninitialized,
|
||||
toCorporation: uninitialized,
|
||||
toCreateProgram: uninitialized,
|
||||
toDevMenu: uninitialized,
|
||||
toFaction: uninitialized,
|
||||
toFactions: uninitialized,
|
||||
toGameOptions: uninitialized,
|
||||
toGang: uninitialized,
|
||||
toHacknetNodes: uninitialized,
|
||||
toInfiltration: uninitialized,
|
||||
toJob: uninitialized,
|
||||
toMilestones: uninitialized,
|
||||
toResleeves: uninitialized,
|
||||
toScriptEditor: uninitialized,
|
||||
toSleeves: uninitialized,
|
||||
toStockMarket: uninitialized,
|
||||
toTerminal: uninitialized,
|
||||
toTravel: uninitialized,
|
||||
toTutorial: uninitialized,
|
||||
toWork: uninitialized,
|
||||
toBladeburnerCinematic: uninitialized,
|
||||
toLocation: uninitialized,
|
||||
toStaneksGift: uninitialized,
|
||||
toAchievements: uninitialized,
|
||||
toThemeBrowser: uninitialized,
|
||||
toImportSave: uninitialized,
|
||||
};
|
||||
|
||||
function determineStartPage(player: IPlayer): Page {
|
||||
@ -223,6 +162,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
const [{ files, vim }, setEditorOptions] = useState({ files: {}, vim: false });
|
||||
const [page, setPage] = useState(determineStartPage(player));
|
||||
const setRerender = useState(0)[1];
|
||||
const [augPage, setAugPage] = useState<boolean>(false);
|
||||
const [faction, setFaction] = useState<Faction>(
|
||||
player.currentWorkFactionName ? Factions[player.currentWorkFactionName] : (undefined as unknown as Faction),
|
||||
);
|
||||
@ -266,6 +206,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
}
|
||||
|
||||
Router = {
|
||||
isInitialized: true,
|
||||
page: () => page,
|
||||
allowRouting: (value: boolean) => setAllowRoutingCalls(value),
|
||||
toActiveScripts: () => setPage(Page.ActiveScripts),
|
||||
@ -275,7 +216,8 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
toCorporation: () => setPage(Page.Corporation),
|
||||
toCreateProgram: () => setPage(Page.CreateProgram),
|
||||
toDevMenu: () => setPage(Page.DevMenu),
|
||||
toFaction: (faction?: Faction) => {
|
||||
toFaction: (faction: Faction, augPage = false) => {
|
||||
setAugPage(augPage);
|
||||
setPage(Page.Faction);
|
||||
if (faction) setFaction(faction);
|
||||
},
|
||||
@ -342,12 +284,11 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Wrap Router navigate functions to be able to disable the execution
|
||||
_functions(Router).
|
||||
filter((fnName) => fnName.startsWith('to')).
|
||||
forEach((fnName) => {
|
||||
_functions(Router)
|
||||
.filter((fnName) => fnName.startsWith("to"))
|
||||
.forEach((fnName) => {
|
||||
// @ts-ignore - tslint does not like this, couldn't find a way to make it cooperate
|
||||
Router[fnName] = _wrap(Router[fnName], (func, ...args) => {
|
||||
if (!allowRoutingCalls) {
|
||||
@ -356,7 +297,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the function normally
|
||||
// Call the function normally
|
||||
return func(...args);
|
||||
});
|
||||
});
|
||||
@ -453,7 +394,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
break;
|
||||
}
|
||||
case Page.Faction: {
|
||||
mainPage = <FactionRoot faction={faction} />;
|
||||
mainPage = <FactionRoot faction={faction} augPage={augPage} />;
|
||||
break;
|
||||
}
|
||||
case Page.Milestones: {
|
||||
@ -564,13 +505,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
break;
|
||||
}
|
||||
case Page.ImportSave: {
|
||||
mainPage = (
|
||||
<ImportSaveRoot
|
||||
importString={importString}
|
||||
automatic={importAutomatic}
|
||||
router={Router}
|
||||
/>
|
||||
);
|
||||
mainPage = <ImportSaveRoot importString={importString} automatic={importAutomatic} router={Router} />;
|
||||
withSidebar = false;
|
||||
withPopups = false;
|
||||
bypassGame = true;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { EventEmitter } from "../../utils/EventEmitter";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { Modal } from "./Modal";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
import {sha256} from "js-sha256";
|
||||
|
@ -4,7 +4,7 @@ import React, { useState, useEffect } from "react";
|
||||
import { Theme, useTheme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
import { Reputation } from "./Reputation";
|
||||
import { KillScriptsModal } from "./KillScriptsModal";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Hashes } from "../../ui/React/Hashes";
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
import { Hashes } from "./Hashes";
|
||||
|
||||
export function HashRate({ hashes }: { hashes: number }): React.ReactElement {
|
||||
return <Hashes hashes={`${numeralWrapper.formatHashes(hashes)} h / s`} />;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
|
@ -288,4 +288,4 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
</Paper>
|
||||
</Draggable>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
import { Money } from "./Money";
|
||||
|
||||
export function MoneyRate({ money }: { money: number }): JSX.Element {
|
||||
return <Money money={`${numeralWrapper.formatMoney(money)} / sec`} />;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { EventEmitter } from "../../utils/EventEmitter";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { Modal } from "./Modal";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
import { Reputation } from "./Reputation";
|
||||
|
||||
export function ReputationRate({ reputation }: { reputation: number }): React.ReactElement {
|
||||
return <Reputation reputation={`${numeralWrapper.formatReputation(reputation)} / sec`} />;
|
||||
|
@ -54,6 +54,7 @@ export interface IRouter {
|
||||
// toMission(): void;
|
||||
// toRedPill(): void;
|
||||
// toworkInProgress(): void;
|
||||
isInitialized: boolean;
|
||||
page(): Page;
|
||||
allowRouting(value: boolean): void;
|
||||
toActiveScripts(): void;
|
||||
@ -65,7 +66,7 @@ export interface IRouter {
|
||||
toCorporation(): void;
|
||||
toCreateProgram(): void;
|
||||
toDevMenu(): void;
|
||||
toFaction(faction?: Faction): void; // faction name
|
||||
toFaction(faction: Faction, augPage?: boolean): void; // faction name
|
||||
toFactions(): void;
|
||||
toGameOptions(): void;
|
||||
toGang(): void;
|
||||
|
@ -31,10 +31,35 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
const id = setInterval(rerender, CONSTANTS.MilliPerCycle);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
const player = use.Player();
|
||||
const router = use.Router();
|
||||
const faction = Factions[player.currentWorkFactionName];
|
||||
|
||||
if (player.workType == CONSTANTS.WorkTypeFaction) {
|
||||
const faction = Factions[player.currentWorkFactionName];
|
||||
if (!faction) {
|
||||
return (
|
||||
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
|
||||
<Grid item>
|
||||
<Typography>
|
||||
Something has gone wrong, you cannot work for {player.currentWorkFactionName || "(Faction not found)"} at
|
||||
this time.
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
if (!faction) {
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h4" color="primary">
|
||||
You have not joined {faction} yet!
|
||||
</Typography>
|
||||
<Button onClick={() => router.toFactions()}>Back to Factions</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function cancel(): void {
|
||||
router.toFaction(faction);
|
||||
player.finishFactionWork(true);
|
||||
@ -124,7 +149,6 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
}
|
||||
|
||||
function unfocus(): void {
|
||||
router.toFaction(faction);
|
||||
router.toCity();
|
||||
player.stopFocusing();
|
||||
}
|
||||
|
@ -75,4 +75,4 @@ export function subsetOf<Type, Key extends keyof Type, Value>(options: Value[]):
|
||||
}
|
||||
obj[key] = validValues as unknown as Type[Key];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user