mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-18 12:15:44 +01:00
Merge branch 'dev' of https://github.com/deathly809/bitburner into dev
This commit is contained in:
commit
0002daa3e9
8
.github/ISSUE_TEMPLATE
vendored
Normal file
8
.github/ISSUE_TEMPLATE
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# include (where relevant)
|
||||
|
||||
You can delete this section
|
||||
|
||||
- [ ] Save file
|
||||
- [ ] Minimal scripts to reproduce the issue
|
||||
- [ ] Steps to reproduce
|
||||
- [ ] Version of the game, e.g. Bitburner v1.3.0 (216bf616)
|
9
.github/PULL_REQUEST_TEMPLATE
vendored
Normal file
9
.github/PULL_REQUEST_TEMPLATE
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# Documentation
|
||||
|
||||
- DO NOT CHANGE any markdown/\*.md, these files are autogenerated from NetscriptDefinitions.d.ts and will be overwritten
|
||||
- DO NOT re-generate the documentation, makes it harder to review.
|
||||
|
||||
# Bug fix
|
||||
|
||||
- Include how it was tested
|
||||
- Include screenshot / gif (if possible)
|
34
dist/vendor.bundle.js
vendored
34
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@ -3,6 +3,9 @@ const greenworks = require("./greenworks");
|
||||
const log = require("electron-log");
|
||||
|
||||
function enableAchievementsInterval(window) {
|
||||
// If the Steam API could not be initialized on game start, we'll abort this.
|
||||
if (global.greenworksError) return;
|
||||
|
||||
// This is backward but the game fills in an array called `document.achievements` and we retrieve it from
|
||||
// here. Hey if it works it works.
|
||||
const steamAchievements = greenworks.getAchievementNames();
|
||||
|
@ -16,10 +16,18 @@ process.on('uncaughtException', function () {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
if (greenworks.init()) {
|
||||
log.info("Steam API has been initialized.");
|
||||
} else {
|
||||
log.warn("Steam API has failed to initialize.");
|
||||
// We want to fail gracefully if we cannot connect to Steam
|
||||
try {
|
||||
if (greenworks.init()) {
|
||||
log.info("Steam API has been initialized.");
|
||||
} else {
|
||||
const error = "Steam API has failed to initialize.";
|
||||
log.warn(error);
|
||||
global.greenworksError = error;
|
||||
}
|
||||
} catch (ex) {
|
||||
log.warn(ex.message);
|
||||
global.greenworksError = ex.message;
|
||||
}
|
||||
|
||||
function setStopProcessHandler(app, window, enabled) {
|
||||
@ -113,4 +121,13 @@ app.whenReady().then(async () => {
|
||||
} else {
|
||||
startWindow(process.argv.includes("--no-scripts"));
|
||||
}
|
||||
|
||||
if (global.greenworksError) {
|
||||
dialog.showMessageBox({
|
||||
title: 'Bitburner',
|
||||
message: 'Could not connect to Steam',
|
||||
detail: `${global.greenworksError}\n\nYou won't be able to receive achievements until this is resolved and you restart the game.`,
|
||||
type: 'warning', buttons: ['OK']
|
||||
});
|
||||
}
|
||||
});
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
23
markdown/bitburner.userinterface.getstyles.md
Normal file
23
markdown/bitburner.userinterface.getstyles.md
Normal file
@ -0,0 +1,23 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [UserInterface](./bitburner.userinterface.md) > [getStyles](./bitburner.userinterface.getstyles.md)
|
||||
|
||||
## UserInterface.getStyles() method
|
||||
|
||||
Get the current styles
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getStyles(): IStyleSettings;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
IStyleSettings
|
||||
|
||||
An object containing the player's styles
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: cost: 0 GB
|
||||
|
@ -16,7 +16,10 @@ interface UserInterface
|
||||
|
||||
| Method | Description |
|
||||
| --- | --- |
|
||||
| [getStyles()](./bitburner.userinterface.getstyles.md) | Get the current styles |
|
||||
| [getTheme()](./bitburner.userinterface.gettheme.md) | Get the current theme |
|
||||
| [resetStyles()](./bitburner.userinterface.resetstyles.md) | Resets the player's styles to the default values |
|
||||
| [resetTheme()](./bitburner.userinterface.resettheme.md) | Resets the player's theme to the default values |
|
||||
| [setStyles(newStyles)](./bitburner.userinterface.setstyles.md) | Sets the current styles |
|
||||
| [setTheme(newTheme)](./bitburner.userinterface.settheme.md) | Sets the current theme |
|
||||
|
||||
|
21
markdown/bitburner.userinterface.resetstyles.md
Normal file
21
markdown/bitburner.userinterface.resetstyles.md
Normal file
@ -0,0 +1,21 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [UserInterface](./bitburner.userinterface.md) > [resetStyles](./bitburner.userinterface.resetstyles.md)
|
||||
|
||||
## UserInterface.resetStyles() method
|
||||
|
||||
Resets the player's styles to the default values
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
resetStyles(): void;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
void
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: cost: 0 GB
|
||||
|
38
markdown/bitburner.userinterface.setstyles.md
Normal file
38
markdown/bitburner.userinterface.setstyles.md
Normal file
@ -0,0 +1,38 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [UserInterface](./bitburner.userinterface.md) > [setStyles](./bitburner.userinterface.setstyles.md)
|
||||
|
||||
## UserInterface.setStyles() method
|
||||
|
||||
Sets the current styles
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
setStyles(newStyles: IStyleSettings): void;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| newStyles | IStyleSettings | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
void
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: cost: 0 GB
|
||||
|
||||
## Example
|
||||
|
||||
Usage example (NS2)
|
||||
|
||||
```ts
|
||||
const styles = ns.ui.getStyles();
|
||||
styles.fontFamily = 'Comic Sans Ms';
|
||||
ns.ui.setStyles(styles);
|
||||
```
|
||||
|
@ -2582,7 +2582,6 @@ function installAugmentations(): boolean {
|
||||
augmentationList += aug.name + level + "<br>";
|
||||
}
|
||||
Player.queuedAugmentations = [];
|
||||
|
||||
dialogBoxCreate(
|
||||
"You slowly drift to sleep as scientists put you under in order " +
|
||||
"to install the following Augmentations:<br>" +
|
||||
|
@ -12,7 +12,7 @@ export const ConsoleHelpText: {
|
||||
} = {
|
||||
helpList: [
|
||||
"Use 'help [command]' to get more information about a particular Bladeburner console command.",
|
||||
"",
|
||||
" ",
|
||||
" automate [var] [val] [hi/low] Configure simple automation for Bladeburner tasks",
|
||||
" clear/cls Clear the console",
|
||||
" help [cmd] Display this help text, or help text for a specific command",
|
||||
@ -20,96 +20,103 @@ export const ConsoleHelpText: {
|
||||
" skill [action] [name] Level or display info about your Bladeburner skills",
|
||||
" start [type] [name] Start a Bladeburner action/task",
|
||||
" stop Stops your current Bladeburner action/task",
|
||||
" ",
|
||||
],
|
||||
automate: [
|
||||
"automate [var] [val] [hi/low]",
|
||||
"",
|
||||
"Usage: automate [var] [val] [hi/low]",
|
||||
" ",
|
||||
"A simple way to automate your Bladeburner actions. This console command can be used " +
|
||||
"to automatically start an action when your stamina rises above a certain threshold, and " +
|
||||
"automatically switch to another action when your stamina drops below another threshold.",
|
||||
" ",
|
||||
" automate status - Check the current status of your automation and get a brief description of what it'll do",
|
||||
" automate en - Enable the automation feature",
|
||||
" automate dis - Disable the automation feature",
|
||||
"",
|
||||
" ",
|
||||
"There are four properties that must be set for this automation to work properly. Here is how to set them:",
|
||||
"",
|
||||
" ",
|
||||
" automate stamina 100 high",
|
||||
" automate contract Tracking high",
|
||||
" automate stamina 50 low",
|
||||
" automate general 'Field Analysis' low",
|
||||
"",
|
||||
" ",
|
||||
"Using the four console commands above will set the automation to perform Tracking contracts " +
|
||||
"if your stamina is 100 or higher, and then switch to Field Analysis if your stamina drops below " +
|
||||
"50. Note that when setting the action, the name of the action is CASE-SENSITIVE. It must " +
|
||||
"exactly match whatever the name is in the UI.",
|
||||
" ",
|
||||
],
|
||||
clear: ["clear", "", "Clears the console"],
|
||||
cls: ["cls", "", "Clears the console"],
|
||||
clear: ["Usage: clear", " ", "Clears the console", " "],
|
||||
cls: ["Usage: cls", " ", "Clears the console", " "],
|
||||
help: [
|
||||
"help [command]",
|
||||
"",
|
||||
"Usage: help [command]",
|
||||
" ",
|
||||
"Running 'help' with no arguments displays the general help text, which lists all console commands " +
|
||||
"and a brief description of what they do. A command can be specified to get more specific help text " +
|
||||
"about that particular command. For example:",
|
||||
"",
|
||||
" ",
|
||||
" help automate",
|
||||
"",
|
||||
" ",
|
||||
"will display specific information about using the automate console command",
|
||||
" ",
|
||||
],
|
||||
log: [
|
||||
"log [en/dis] [type]",
|
||||
"",
|
||||
"Usage: log [en/dis] [type]",
|
||||
" ",
|
||||
"Enable or disable logging. By default, the results of completing actions such as contracts/operations are logged " +
|
||||
"in the console. There are also random events that are logged in the console as well. The five categories of " +
|
||||
"things that get logged are:",
|
||||
"",
|
||||
" ",
|
||||
"[general, contracts, ops, blackops, events]",
|
||||
"",
|
||||
" ",
|
||||
"The logging for these categories can be enabled or disabled like so:",
|
||||
"",
|
||||
" ",
|
||||
" log dis contracts - Disables logging that occurs when contracts are completed",
|
||||
" log en contracts - Enables logging that occurs when contracts are completed",
|
||||
" log dis events - Disables logging for Bladeburner random events",
|
||||
"",
|
||||
" ",
|
||||
"Logging can be universally enabled/disabled using the 'all' keyword:",
|
||||
"",
|
||||
" ",
|
||||
" log dis all",
|
||||
" log en all",
|
||||
" ",
|
||||
],
|
||||
skill: [
|
||||
"skill [action] [name]",
|
||||
"",
|
||||
"Usage: skill [action] [name]",
|
||||
" ",
|
||||
"Level or display information about your skills.",
|
||||
"",
|
||||
" ",
|
||||
"To display information about all of your skills and your multipliers, use:",
|
||||
"",
|
||||
" ",
|
||||
" skill list",
|
||||
"",
|
||||
" ",
|
||||
"To display information about a specific skill, specify the name of the skill afterwards. " +
|
||||
"Note that the name of the skill is case-sensitive. Enter it exactly as seen in the UI. If " +
|
||||
"the name of the skill has whitespace, enclose the name of the skill in double quotation marks:",
|
||||
"",
|
||||
" ",
|
||||
" skill list Reaper",
|
||||
" skill list 'Digital Observer'",
|
||||
"",
|
||||
" ",
|
||||
"This console command can also be used to level up skills:",
|
||||
"",
|
||||
" ",
|
||||
" skill level [skill name]",
|
||||
" ",
|
||||
],
|
||||
start: [
|
||||
"start [type] [name]",
|
||||
"",
|
||||
"Usage: start [type] [name]",
|
||||
" ",
|
||||
"Start an action. An action is specified by its type and its name. The " +
|
||||
"name is case-sensitive. It must appear exactly as it does in the UI. If " +
|
||||
"the name of the action has whitespace, enclose it in double quotation marks. " +
|
||||
"Valid action types include:",
|
||||
"",
|
||||
" ",
|
||||
"[general, contract, op, blackop]",
|
||||
"",
|
||||
" ",
|
||||
"Examples:",
|
||||
"",
|
||||
" ",
|
||||
" start contract Tracking",
|
||||
" start op 'Undercover Operation'",
|
||||
" ",
|
||||
],
|
||||
stop: ["stop", "", "Stop your current action and go idle."],
|
||||
stop: ["Usage: stop", " ", "Stop your current action and go idle.", " "],
|
||||
};
|
||||
|
@ -4,7 +4,6 @@ import { Console } from "./Console";
|
||||
import { AllPages } from "./AllPages";
|
||||
|
||||
import { use } from "../../ui/Context";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
export function BladeburnerRoot(): React.ReactElement {
|
||||
@ -24,14 +23,10 @@ export function BladeburnerRoot(): React.ReactElement {
|
||||
if (bladeburner === null) return <></>;
|
||||
return (
|
||||
<Box display="flex" flexDirection="column">
|
||||
<Grid container>
|
||||
<Grid item xs={6}>
|
||||
<Stats bladeburner={bladeburner} player={player} router={router} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Console bladeburner={bladeburner} player={player} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "4fr 8fr", p: 1 }}>
|
||||
<Stats bladeburner={bladeburner} player={player} router={router} />
|
||||
<Console bladeburner={bladeburner} player={player} />
|
||||
</Box>
|
||||
|
||||
<AllPages bladeburner={bladeburner} player={player} />
|
||||
</Box>
|
||||
|
@ -23,7 +23,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
width: "100%",
|
||||
},
|
||||
input: {
|
||||
backgroundColor: "#000",
|
||||
backgroundColor: theme.colors.backgroundsecondary,
|
||||
},
|
||||
nopadding: {
|
||||
padding: theme.spacing(0),
|
||||
@ -56,6 +56,7 @@ export function Console(props: IProps): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
const [command, setCommand] = useState("");
|
||||
const setRerender = useState(false)[1];
|
||||
const consoleInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
function handleCommandChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setCommand(event.target.value);
|
||||
@ -131,15 +132,21 @@ export function Console(props: IProps): React.ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
function handleClick(): void {
|
||||
if (!consoleInput.current) return;
|
||||
consoleInput.current.focus();
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Box sx={{
|
||||
height: '60vh',
|
||||
paddingBottom: '8px',
|
||||
display: 'flex',
|
||||
alignItems: 'stretch',
|
||||
whiteSpace: 'pre-wrap',
|
||||
}}>
|
||||
}}
|
||||
onClick={handleClick}>
|
||||
<Box>
|
||||
<Logs entries={[...props.bladeburner.consoleLogs]} />
|
||||
</Box>
|
||||
@ -149,6 +156,7 @@ export function Console(props: IProps): React.ReactElement {
|
||||
autoFocus
|
||||
tabIndex={1}
|
||||
type="text"
|
||||
inputRef={consoleInput}
|
||||
value={command}
|
||||
onChange={handleCommandChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
@ -171,7 +179,7 @@ interface ILogProps {
|
||||
entries: string[];
|
||||
}
|
||||
|
||||
function Logs({entries}: ILogProps): React.ReactElement {
|
||||
function Logs({ entries }: ILogProps): React.ReactElement {
|
||||
const scrollHook = useRef<HTMLUListElement>(null);
|
||||
|
||||
// TODO: Text gets shifted up as new entries appear, if the user scrolled up it should attempt to keep the text focused
|
||||
@ -182,7 +190,7 @@ function Logs({entries}: ILogProps): React.ReactElement {
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [entries]);
|
||||
}, [entries.length]);
|
||||
|
||||
return (
|
||||
<List sx={{ height: "100%", overflow: "auto", p: 1 }} ref={scrollHook}>
|
||||
|
@ -3,7 +3,6 @@ import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/Stri
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { StatsTable } from "../../ui/React/StatsTable";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
@ -44,138 +43,142 @@ export function Stats(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Box display="flex">
|
||||
<Tooltip title={<Typography>Your rank within the Bladeburner division.</Typography>}>
|
||||
<Typography>Rank: {formatNumber(props.bladeburner.rank, 2)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Performing actions will use up your stamina.
|
||||
<br />
|
||||
<br />
|
||||
Your max stamina is determined primarily by your agility stat.
|
||||
<br />
|
||||
<br />
|
||||
Your stamina gain rate is determined by both your agility and your max stamina. Higher max stamina leads
|
||||
to a higher gain rate.
|
||||
<br />
|
||||
<br />
|
||||
Once your stamina falls below 50% of its max value, it begins to negatively affect the success rate of
|
||||
your contracts/operations. This penalty is shown in the overview panel. If the penalty is 15%, then this
|
||||
means your success rate would be multipled by 85% (100 - 15).
|
||||
<br />
|
||||
<br />
|
||||
Your max stamina and stamina gain rate can also be increased by training, or through skills and
|
||||
Augmentation upgrades.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<Typography>
|
||||
Stamina Penalty: {formatNumber((1 - props.bladeburner.calculateStaminaPenalty()) * 100, 1)}%
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</Typography>
|
||||
<Typography>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</Typography>
|
||||
<br />
|
||||
<Typography>Num Times Hospitalized: {props.bladeburner.numHosp}</Typography>
|
||||
<Typography>
|
||||
Money Lost From Hospitalizations: <Money money={props.bladeburner.moneyLost} />
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>Current City: {props.bladeburner.city}</Typography>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
This is your Bladeburner division's estimate of how many Synthoids exist in your current city. An accurate
|
||||
population count increases success rate estimates.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a
|
||||
chaos level can make contracts and operations harder.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
{(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000 > 15000 && (
|
||||
<>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by
|
||||
browser). Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Paper sx={{ p: 1, overflowY: 'auto', overflowX: 'hidden', wordBreak: 'break-all' }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, maxHeight: '60vh' }}>
|
||||
<Box sx={{ alignSelf: 'flex-start', width: '100%' }}>
|
||||
<Button onClick={() => setTravelOpen(true)} sx={{ width: '50%' }}>Travel</Button>
|
||||
<Tooltip title={!inFaction ? <Typography>Rank 25 required.</Typography> : ""}>
|
||||
<span>
|
||||
<Button disabled={!inFaction} onClick={openFaction} sx={{ width: '50%' }}>
|
||||
Faction
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<TravelModal open={travelOpen} onClose={() => setTravelOpen(false)} bladeburner={props.bladeburner} />
|
||||
</Box>
|
||||
<Box display="flex">
|
||||
<Tooltip title={<Typography>Your rank within the Bladeburner division.</Typography>}>
|
||||
<Typography>Rank: {formatNumber(props.bladeburner.rank, 2)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Bonus time:{" "}
|
||||
{convertTimeMsToTimeElapsedString(
|
||||
(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000,
|
||||
)}
|
||||
Performing actions will use up your stamina.
|
||||
<br />
|
||||
<br />
|
||||
Your max stamina is determined primarily by your agility stat.
|
||||
<br />
|
||||
<br />
|
||||
Your stamina gain rate is determined by both your agility and your max stamina. Higher max stamina leads
|
||||
to a higher gain rate.
|
||||
<br />
|
||||
<br />
|
||||
Once your stamina falls below 50% of its max value, it begins to negatively affect the success rate of
|
||||
your contracts/operations. This penalty is shown in the overview panel. If the penalty is 15%, then this
|
||||
means your success rate would be multipled by 85% (100 - 15).
|
||||
<br />
|
||||
<br />
|
||||
Your max stamina and stamina gain rate can also be increased by training, or through skills and
|
||||
Augmentation upgrades.
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<Typography>
|
||||
Stamina Penalty: {formatNumber((1 - props.bladeburner.calculateStaminaPenalty()) * 100, 1)}%
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</Typography>
|
||||
<Typography>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</Typography>
|
||||
<br />
|
||||
<Typography>Num Times Hospitalized: {props.bladeburner.numHosp}</Typography>
|
||||
<Typography>
|
||||
Money Lost From Hospitalizations: <Money money={props.bladeburner.moneyLost} />
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>Current City: {props.bladeburner.city}</Typography>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
This is your Bladeburner division's estimate of how many Synthoids exist in your current city. An accurate
|
||||
population count increases success rate estimates.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a
|
||||
chaos level can make contracts and operations harder.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
{(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000 > 15000 && (
|
||||
<>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by
|
||||
browser). Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Bonus time:{" "}
|
||||
{convertTimeMsToTimeElapsedString(
|
||||
(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000,
|
||||
)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Aug. Success Chance mult: {formatNumber(props.player.bladeburner_success_chance_mult * 100, 1)}%
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
|
||||
<br />
|
||||
<StatsTable
|
||||
rows={[
|
||||
["Aug. Success Chance mult: ", formatNumber(props.player.bladeburner_success_chance_mult * 100, 1) + "%"],
|
||||
["Aug. Max Stamina mult: ", formatNumber(props.player.bladeburner_max_stamina_mult * 100, 1) + "%"],
|
||||
["Aug. Stamina Gain mult: ", formatNumber(props.player.bladeburner_stamina_gain_mult * 100, 1) + "%"],
|
||||
["Aug. Field Analysis mult: ", formatNumber(props.player.bladeburner_analysis_mult * 100, 1) + "%"],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
<Button onClick={() => setTravelOpen(true)}>Travel</Button>
|
||||
<Tooltip title={!inFaction ? <Typography>Rank 25 required.</Typography> : ""}>
|
||||
<span>
|
||||
<Button disabled={!inFaction} onClick={openFaction}>
|
||||
Faction
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<TravelModal open={travelOpen} onClose={() => setTravelOpen(false)} bladeburner={props.bladeburner} />
|
||||
Aug. Max Stamina mult: {formatNumber(props.player.bladeburner_max_stamina_mult * 100, 1)}%
|
||||
<br />
|
||||
Aug. Stamina Gain mult: {formatNumber(props.player.bladeburner_stamina_gain_mult * 100, 1)}%
|
||||
<br />
|
||||
Aug. Field Analysis mult: {formatNumber(props.player.bladeburner_analysis_mult * 100, 1)}%
|
||||
</Typography>
|
||||
</Box>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import { hasAugmentationPrereqs } from "../FactionHelpers";
|
||||
import { use } from "../../ui/Context";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
import { Favor } from "../../ui/React/Favor";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
@ -203,7 +204,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Price multiplier: x {mult.toFixed(3)}</Typography>
|
||||
<Typography>Price multiplier: x {numeralWrapper.formatMultiplier(mult)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
|
||||
|
@ -364,6 +364,9 @@ export const RamCosts: IMap<any> = {
|
||||
getTheme: 0,
|
||||
setTheme: 0,
|
||||
resetTheme: 0,
|
||||
getStyles: 0,
|
||||
setStyles: 0,
|
||||
resetStyles: 0,
|
||||
},
|
||||
|
||||
heart: {
|
||||
|
@ -172,7 +172,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`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -432,8 +432,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
throw makeRuntimeErrorMsg(funcName, `${argName} should be a string`);
|
||||
},
|
||||
number: (funcName: string, argName: string, v: any): number => {
|
||||
if (!isNaN(v))
|
||||
{
|
||||
if (!isNaN(v)) {
|
||||
if (typeof v === "number") return v;
|
||||
if (!isNaN(parseFloat(v))) return parseFloat(v);
|
||||
}
|
||||
@ -700,8 +699,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;
|
||||
|
@ -53,23 +53,22 @@ export function NetscriptCodingContract(
|
||||
const serv = helper.getServer(hostname, "codingcontract.attempt");
|
||||
if (contract.isSolution(answer)) {
|
||||
const reward = player.gainCodingContractReward(creward, contract.getDifficulty());
|
||||
workerScript.log("attempt", () => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`);
|
||||
workerScript.log("codingcontract.attempt", () => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`);
|
||||
serv.removeContract(filename);
|
||||
return returnReward ? reward : true;
|
||||
} else {
|
||||
++contract.tries;
|
||||
if (contract.tries >= contract.getMaxNumTries()) {
|
||||
workerScript.log(
|
||||
"attempt",
|
||||
"codingcontract.attempt",
|
||||
() => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`,
|
||||
);
|
||||
serv.removeContract(filename);
|
||||
} else {
|
||||
workerScript.log(
|
||||
"attempt",
|
||||
"codingcontract.attempt",
|
||||
() =>
|
||||
`Coding Contract attempt '${filename}' failed. ${
|
||||
contract.getMaxNumTries() - contract.tries
|
||||
`Coding Contract attempt '${filename}' failed. ${contract.getMaxNumTries() - contract.tries
|
||||
} attempts remaining.`,
|
||||
);
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
|
||||
}
|
||||
const node = getHacknetNode(i, "upgradeCache");
|
||||
if (!(node instanceof HacknetServer)) {
|
||||
workerScript.log("upgradeCache", () => "Can only be called on hacknet servers");
|
||||
workerScript.log("hacknet.upgradeCache", () => "Can only be called on hacknet servers");
|
||||
return false;
|
||||
}
|
||||
const res = purchaseCacheUpgrade(player, node, n);
|
||||
@ -138,7 +138,7 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
|
||||
}
|
||||
const node = getHacknetNode(i, "upgradeCache");
|
||||
if (!(node instanceof HacknetServer)) {
|
||||
workerScript.log("getCacheUpgradeCost", () => "Can only be called on hacknet servers");
|
||||
workerScript.log("hacknet.getCacheUpgradeCost", () => "Can only be called on hacknet servers");
|
||||
return -1;
|
||||
}
|
||||
return node.calculateCacheUpgradeCost(n);
|
||||
|
@ -2,10 +2,11 @@ import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import { UserInterface as IUserInterface, UserInterfaceTheme } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { IStyleSettings, UserInterface as IUserInterface, UserInterfaceTheme } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { ThemeEvents } from "../ui/React/Theme";
|
||||
import { defaultTheme } from "../Settings/Themes";
|
||||
import { defaultStyles } from "../Settings/Styles";
|
||||
|
||||
export function NetscriptUserInterface(
|
||||
player: IPlayer,
|
||||
@ -18,6 +19,11 @@ export function NetscriptUserInterface(
|
||||
return { ...Settings.theme };
|
||||
},
|
||||
|
||||
getStyles: function (): IStyleSettings {
|
||||
helper.updateDynamicRam("getStyles", getRamCost(player, "ui", "getStyles"));
|
||||
return { ...Settings.styles };
|
||||
},
|
||||
|
||||
setTheme: function (newTheme: UserInterfaceTheme): void {
|
||||
helper.updateDynamicRam("setTheme", getRamCost(player, "ui", "setTheme"));
|
||||
const hex = /^(#)((?:[A-Fa-f0-9]{3}){1,2})$/;
|
||||
@ -43,11 +49,41 @@ export function NetscriptUserInterface(
|
||||
}
|
||||
},
|
||||
|
||||
setStyles: function (newStyles: IStyleSettings): void {
|
||||
helper.updateDynamicRam("setStyles", getRamCost(player, "ui", "setStyles"));
|
||||
|
||||
const currentStyles = {...Settings.styles}
|
||||
const errors: string[] = [];
|
||||
for (const key of Object.keys(newStyles)) {
|
||||
if (!((currentStyles as any)[key])) {
|
||||
// Invalid key
|
||||
errors.push(`Invalid key "${key}"`);
|
||||
} else {
|
||||
(currentStyles as any)[key] = (newStyles as any)[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length === 0) {
|
||||
Object.assign(Settings.styles, currentStyles);
|
||||
ThemeEvents.emit();
|
||||
workerScript.log("ui.setStyles", () => `Successfully set styles`);
|
||||
} else {
|
||||
workerScript.log("ui.setStyles", () => `Failed to set styles. Errors: ${errors.join(', ')}`);
|
||||
}
|
||||
},
|
||||
|
||||
resetTheme: function (): void {
|
||||
helper.updateDynamicRam("resetTheme", getRamCost(player, "ui", "resetTheme"));
|
||||
Settings.theme = defaultTheme;
|
||||
Settings.theme = { ...defaultTheme };
|
||||
ThemeEvents.emit();
|
||||
workerScript.log("ui.resetTheme", () => `Reinitialized theme to default`);
|
||||
},
|
||||
|
||||
resetStyles: function (): void {
|
||||
helper.updateDynamicRam("resetStyles", getRamCost(player, "ui", "resetStyles"));
|
||||
Settings.styles = { ...defaultStyles };
|
||||
ThemeEvents.emit();
|
||||
workerScript.log("ui.resetStyles", () => `Reinitialized styles to default`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -946,11 +946,6 @@ export function workForFaction(this: IPlayer, numCycles: number): boolean {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
let favorMult = 1 + faction.favor / 100;
|
||||
if (isNaN(favorMult)) {
|
||||
favorMult = 1;
|
||||
}
|
||||
this.workRepGainRate *= favorMult;
|
||||
this.workRepGainRate *= BitNodeMultipliers.FactionWorkRepGain;
|
||||
|
||||
//Cap the number of cycles being processed to whatever would put you at limit (20 hours)
|
||||
|
@ -161,8 +161,6 @@ export function prestigeAugmentation(): void {
|
||||
resetPidCounter();
|
||||
ProgramsSeen.splice(0, ProgramsSeen.length);
|
||||
InvitationsSeen.splice(0, InvitationsSeen.length);
|
||||
|
||||
Router.clearHistory();
|
||||
}
|
||||
|
||||
// Prestige by destroying Bit Node and gaining a Source File
|
||||
@ -308,7 +306,5 @@ export function prestigeSourceFile(flume: boolean): void {
|
||||
// Gain int exp
|
||||
if (SourceFileFlags[5] !== 0 && !flume) Player.gainIntelligenceExp(300);
|
||||
|
||||
Router.clearHistory();
|
||||
|
||||
resetPidCounter();
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ export class Script {
|
||||
*/
|
||||
saveScript(player: IPlayer, filename: string, code: string, hostname: string, otherScripts: Script[]): void {
|
||||
// Update code and filename
|
||||
this.code = code.replace(/^\s+|\s+$/g, "");
|
||||
this.code = Script.formatCode(this.code);
|
||||
|
||||
this.filename = filename;
|
||||
this.server = hostname;
|
||||
@ -158,6 +158,15 @@ export class Script {
|
||||
s.rehash();
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats code: Removes the starting & trailing whitespace
|
||||
* @param {string} code - The code to format
|
||||
* @returns The formatted code
|
||||
*/
|
||||
static formatCode(code: string): string {
|
||||
return code.replace(/^\s+|\s+$/g, "");
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.Script = Script;
|
||||
|
39
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
39
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -3880,6 +3880,36 @@ interface UserInterface {
|
||||
* RAM cost: cost: 0 GB
|
||||
*/
|
||||
resetTheme(): void;
|
||||
|
||||
/**
|
||||
* Get the current styles
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
*
|
||||
* @returns An object containing the player's styles
|
||||
*/
|
||||
getStyles(): IStyleSettings;
|
||||
|
||||
/**
|
||||
* Sets the current styles
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
* @example
|
||||
* Usage example (NS2)
|
||||
* ```ts
|
||||
* const styles = ns.ui.getStyles();
|
||||
* styles.fontFamily = 'Comic Sans Ms';
|
||||
* ns.ui.setStyles(styles);
|
||||
* ```
|
||||
*/
|
||||
setStyles(newStyles: IStyleSettings): void;
|
||||
|
||||
/**
|
||||
* Resets the player's styles to the default values
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
*/
|
||||
resetStyles(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -6354,3 +6384,12 @@ interface UserInterfaceTheme {
|
||||
backgroundsecondary: string;
|
||||
button: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface Styles
|
||||
* @internal
|
||||
*/
|
||||
interface IStyleSettings {
|
||||
fontFamily: string;
|
||||
lineHeight: number;
|
||||
}
|
||||
|
@ -686,7 +686,9 @@ export function Root(props: IProps): React.ReactElement {
|
||||
const serverScript = server.scripts.find((s) => s.filename === openScript.fileName);
|
||||
if (serverScript === undefined) return " *";
|
||||
|
||||
return serverScript.code !== openScript.code ? " *" : "";
|
||||
// The server code is stored with its starting & trailing whitespace removed
|
||||
const openScriptFormatted = Script.formatCode(openScript.code);
|
||||
return serverScript.code !== openScriptFormatted ? " *" : "";
|
||||
}
|
||||
|
||||
// Toolbars are roughly 112px:
|
||||
@ -846,7 +848,7 @@ export function Root(props: IProps): React.ReactElement {
|
||||
<span style={{ color: Settings.theme.primary, fontSize: "20px", textAlign: "center" }}>
|
||||
<Typography variant="h4">No open files</Typography>
|
||||
<Typography variant="h5">
|
||||
Use `nano FILENAME` in
|
||||
Use <code>nano FILENAME</code> in
|
||||
<br />
|
||||
the terminal to open files
|
||||
</Typography>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { ISelfInitializer, ISelfLoading } from "../types";
|
||||
import { OwnedAugmentationsOrderSetting, PurchaseAugmentationsOrderSetting } from "./SettingEnums";
|
||||
import { defaultTheme, ITheme } from "./Themes";
|
||||
import { defaultStyles, IStyleSettings } from "./Styles";
|
||||
import { defaultStyles } from "./Styles";
|
||||
import { WordWrapOptions } from "../ScriptEditor/ui/Options";
|
||||
import { OverviewSettings } from "../ui/React/Overview";
|
||||
import { IStyleSettings } from "../ScriptEditor/NetscriptDefinitions";
|
||||
|
||||
/**
|
||||
* Represents the default settings the player could customize.
|
||||
|
@ -1,9 +1,4 @@
|
||||
import React from "react";
|
||||
|
||||
export interface IStyleSettings {
|
||||
fontFamily: React.CSSProperties["fontFamily"];
|
||||
lineHeight: React.CSSProperties["lineHeight"];
|
||||
}
|
||||
import { IStyleSettings } from "../ScriptEditor/NetscriptDefinitions";
|
||||
|
||||
export const defaultStyles: IStyleSettings = {
|
||||
lineHeight: 1.5,
|
||||
|
@ -3,73 +3,74 @@ import { IMap } from "../types";
|
||||
|
||||
export const TerminalHelpText: string[] = [
|
||||
"Type 'help name' to learn more about the command ",
|
||||
"",
|
||||
'alias [-g] [name="value"] Create or display Terminal aliases',
|
||||
"analyze Get information about the current machine ",
|
||||
"backdoor Install a backdoor on the current machine ",
|
||||
"buy [-l/program] Purchase a program through the Dark Web",
|
||||
"cat [file] Display a .msg, .lit, or .txt file",
|
||||
"cd [dir] Change to a new directory",
|
||||
"check [script] [args...] Print a script's logs to Terminal",
|
||||
"clear Clear all text on the terminal ",
|
||||
"cls See 'clear' command ",
|
||||
"connect [hostname] Connects to a remote server",
|
||||
"cp [src] [dst] Copy a file",
|
||||
"download [script/text file] Downloads scripts or text files to your computer",
|
||||
"expr [math expression] Evaluate a mathematical expression",
|
||||
"free Check the machine's memory (RAM) usage",
|
||||
"grow Spoof money in a servers bank account, increasing the amount available.",
|
||||
"hack Hack the current machine",
|
||||
"help [command] Display this help text, or the help text for a command",
|
||||
"home Connect to home computer",
|
||||
"hostname Displays the hostname of the machine",
|
||||
"kill [script/pid] [args...] Stops the specified script on the current server ",
|
||||
"killall Stops all running scripts on the current machine",
|
||||
"ls [dir] [| grep pattern] Displays all files on the machine",
|
||||
"lscpu Displays the number of CPU cores on the machine",
|
||||
"mem [script] [-t n] Displays the amount of RAM required to run the script",
|
||||
"mv [src] [dest] Move/rename a text or script file",
|
||||
"nano [file ...] Text editor - Open up and edit one or more scripts or text files",
|
||||
"ps Display all scripts that are currently running",
|
||||
"rm [file] Delete a file from the server",
|
||||
"run [name] [-t n] [--tail] [args...] Execute a program or script",
|
||||
"scan Prints all immediately-available network connections",
|
||||
"scan-analyze [d] [-a] Prints info for all servers up to <i>d</i> nodes away",
|
||||
"scp [file ...] [server] Copies a file to a destination server",
|
||||
"sudov Shows whether you have root access on this computer",
|
||||
"tail [script] [args...] Displays dynamic logs for the specified script",
|
||||
"top Displays all running scripts and their RAM usage",
|
||||
"unalias [alias name] Deletes the specified alias",
|
||||
"vim [file ...] Text editor - Open up and edit one or more scripts or text files in vim mode",
|
||||
"weaken Reduce the security of the current machine",
|
||||
"wget [url] [target file] Retrieves code/text from a web server",
|
||||
" ",
|
||||
' alias [-g] [name="value"] Create or display Terminal aliases',
|
||||
" analyze Get information about the current machine ",
|
||||
" backdoor Install a backdoor on the current machine ",
|
||||
" buy [-l/-a/program] Purchase a program through the Dark Web",
|
||||
" cat [file] Display a .msg, .lit, or .txt file",
|
||||
" cd [dir] Change to a new directory",
|
||||
" check [script] [args...] Print a script's logs to Terminal",
|
||||
" clear Clear all text on the terminal ",
|
||||
" cls See 'clear' command ",
|
||||
" connect [hostname] Connects to a remote server",
|
||||
" cp [src] [dst] Copy a file",
|
||||
" download [script/text file] Downloads scripts or text files to your computer",
|
||||
" expr [math expression] Evaluate a mathematical expression",
|
||||
" free Check the machine's memory (RAM) usage",
|
||||
" grow Spoof money in a servers bank account, increasing the amount available.",
|
||||
" hack Hack the current machine",
|
||||
" help [command] Display this help text, or the help text for a command",
|
||||
" home Connect to home computer",
|
||||
" hostname Displays the hostname of the machine",
|
||||
" kill [script/pid] [args...] Stops the specified script on the current server ",
|
||||
" killall Stops all running scripts on the current machine",
|
||||
" ls [dir] [| grep pattern] Displays all files on the machine",
|
||||
" lscpu Displays the number of CPU cores on the machine",
|
||||
" mem [script] [-t n] Displays the amount of RAM required to run the script",
|
||||
" mv [src] [dest] Move/rename a text or script file",
|
||||
" nano [file ...] Text editor - Open up and edit one or more scripts or text files",
|
||||
" ps Display all scripts that are currently running",
|
||||
" rm [file] Delete a file from the server",
|
||||
" run [name] [-t n] [--tail] [args...] Execute a program or script",
|
||||
" scan Prints all immediately-available network connections",
|
||||
" scan-analyze [d] [-a] Prints info for all servers up to <i>d</i> nodes away",
|
||||
" scp [file ...] [server] Copies a file to a destination server",
|
||||
" sudov Shows whether you have root access on this computer",
|
||||
" tail [script] [args...] Displays dynamic logs for the specified script",
|
||||
" top Displays all running scripts and their RAM usage",
|
||||
" unalias [alias name] Deletes the specified alias",
|
||||
" vim [file ...] Text editor - Open up and edit one or more scripts or text files in vim mode",
|
||||
" weaken Reduce the security of the current machine",
|
||||
" wget [url] [target file] Retrieves code/text from a web server",
|
||||
" ",
|
||||
];
|
||||
|
||||
export const HelpTexts: IMap<string[]> = {
|
||||
alias: [
|
||||
'alias [-g] [name="value"] ',
|
||||
'Usage: alias [-g] [name="value"] ',
|
||||
" ",
|
||||
"Create or display aliases. An alias enables a replacement of a word with another string. ",
|
||||
"It can be used to abbreviate a commonly used command, or commonly used parts of a command. The NAME ",
|
||||
"of an alias defines the word that will be replaced, while the VALUE defines what it will be replaced by. For example, ",
|
||||
"you could create the alias 'nuke' for the Terminal command 'run NUKE.exe' using the following: ",
|
||||
" ",
|
||||
'alias nuke="run NUKE.exe"',
|
||||
' alias nuke="run NUKE.exe"',
|
||||
" ",
|
||||
"Then, to run the NUKE.exe program you would just have to enter 'nuke' in Terminal rather than the full command. ",
|
||||
"It is important to note that 'default' aliases will only be substituted for the first word of a Terminal command. For ",
|
||||
"example, if the following alias was set: ",
|
||||
" ",
|
||||
'alias worm="HTTPWorm.exe"',
|
||||
' alias worm="HTTPWorm.exe"',
|
||||
" ",
|
||||
"and then you tried to run the following terminal command: ",
|
||||
" ",
|
||||
"run worm",
|
||||
" run worm",
|
||||
" ",
|
||||
"This would fail because the worm alias is not the first word of a Terminal command. To allow an alias to be substituted ",
|
||||
"anywhere in a Terminal command, rather than just the first word, you must set it to be a global alias using the -g flag: ",
|
||||
" ",
|
||||
'alias -g worm="HTTPWorm.exe"',
|
||||
' alias -g worm="HTTPWorm.exe"',
|
||||
" ",
|
||||
"Now, the 'worm' alias will be substituted anytime it shows up as an individual word in a Terminal command. ",
|
||||
" ",
|
||||
@ -80,15 +81,16 @@ export const HelpTexts: IMap<string[]> = {
|
||||
" ",
|
||||
],
|
||||
analyze: [
|
||||
"analyze",
|
||||
"Usage: analyze",
|
||||
" ",
|
||||
"Prints details and statistics about the current server. The information that is printed includes basic ",
|
||||
"server details such as the hostname, whether the player has root access, what ports are opened/closed, and also ",
|
||||
"hacking-related information such as an estimated chance to successfully hack, an estimate of how much money is ",
|
||||
"available on the server, etc.",
|
||||
" ",
|
||||
],
|
||||
backdoor: [
|
||||
"backdoor",
|
||||
"Usage: backdoor",
|
||||
" ",
|
||||
"Install a backdoor on the current machine, grants a secret bonus depending on the machine.",
|
||||
" ",
|
||||
@ -96,7 +98,7 @@ export const HelpTexts: IMap<string[]> = {
|
||||
" ",
|
||||
],
|
||||
buy: [
|
||||
"buy [-l / -a / program]",
|
||||
"Usage: buy [-l / -a / program]",
|
||||
" ",
|
||||
"Purchase a program through the Dark Web. Requires a TOR router to use.",
|
||||
" ",
|
||||
@ -106,65 +108,72 @@ export const HelpTexts: IMap<string[]> = {
|
||||
"If this command is ran with the '-a' flag, it will attempt to purchase all unowned programs.",
|
||||
" ",
|
||||
"Otherwise, the name of the program must be passed in as a parameter. This name is NOT case-sensitive.",
|
||||
" ",
|
||||
],
|
||||
cat: [
|
||||
"cat [file]",
|
||||
"Usage: cat [file]",
|
||||
" ",
|
||||
"Display message (.msg), literature (.lit), or text (.txt) files. Examples:",
|
||||
" ",
|
||||
"cat j1.msg",
|
||||
" cat j1.msg",
|
||||
" ",
|
||||
"cat foo.lit",
|
||||
" cat foo.lit",
|
||||
" ",
|
||||
" cat servers.txt",
|
||||
" ",
|
||||
"cat servers.txt",
|
||||
],
|
||||
cd: [
|
||||
"cd [dir]",
|
||||
"Usage: cd [dir]",
|
||||
" ",
|
||||
"Change to the specified directory. Note that this works even for directories that don't exist. If you ",
|
||||
"change to a directory that does not exist, it will not be 'created'. Examples:",
|
||||
" ",
|
||||
"cd scripts/hacking",
|
||||
" cd scripts/hacking",
|
||||
" ",
|
||||
"cd /logs",
|
||||
" cd /logs",
|
||||
" ",
|
||||
" cd ../",
|
||||
" ",
|
||||
"cd ../",
|
||||
],
|
||||
check: [
|
||||
"check [script name] [args...]",
|
||||
"Usage: check [script name] [args...]",
|
||||
" ",
|
||||
"Print the logs of the script specified by the script name and arguments to the Terminal. Each argument must be separated by ",
|
||||
"a space. Remember that a running script is uniquely ",
|
||||
"identified both by its name and the arguments that are used to start it. So, if a script was ran with the following arguments: ",
|
||||
" ",
|
||||
"run foo.script 1 2 foodnstuff",
|
||||
" run foo.script 1 2 foodnstuff",
|
||||
" ",
|
||||
"Then to run the 'check' command on this script you would have to pass the same arguments in: ",
|
||||
" ",
|
||||
"check foo.script 1 2 foodnstuff",
|
||||
" check foo.script 1 2 foodnstuff",
|
||||
" ",
|
||||
],
|
||||
clear: [
|
||||
"clear",
|
||||
"Usage: clear",
|
||||
" ",
|
||||
"Clear the Terminal screen, deleting all of the text. Note that this does not delete the user's command history, so using the up ",
|
||||
"and down arrow keys is still valid. Also note that this is permanent and there is no way to undo this. Synonymous with 'cls' command",
|
||||
" ",
|
||||
],
|
||||
cls: [
|
||||
"cls",
|
||||
"Usage: cls",
|
||||
" ",
|
||||
"Clear the Terminal screen, deleting all of the text. Note that this does not delete the user's command history, so using the up ",
|
||||
"and down arrow keys is still valid. Also note that this is permanent and there is no way to undo this. Synonymous with 'clear' command",
|
||||
" ",
|
||||
],
|
||||
connect: [
|
||||
"connect [hostname]",
|
||||
"Usage: connect [hostname]",
|
||||
" ",
|
||||
"Connect to a remote server. The hostname or IP address of the remote server must be given as the argument ",
|
||||
"to this command. Note that only servers that are immediately adjacent to the current server in the network can be connected to. To ",
|
||||
"see which servers can be connected to, use the 'scan' command.",
|
||||
" ",
|
||||
],
|
||||
cp: ["cp [src] [dst]", " ", "Copy a file on this server. To copy a file to another server use scp."],
|
||||
cp: ["Usage: cp [src] [dst]", " ", "Copy a file on this server. To copy a file to another server use scp.", " "],
|
||||
download: [
|
||||
"download [script/text file]",
|
||||
"Usage: download [script/text file]",
|
||||
" ",
|
||||
"Downloads a script or text file to your computer (like your real life computer).",
|
||||
" ",
|
||||
@ -178,7 +187,7 @@ export const HelpTexts: IMap<string[]> = {
|
||||
" ",
|
||||
],
|
||||
expr: [
|
||||
"expr [mathematical expression]",
|
||||
"Usage: expr [mathematical expression]",
|
||||
" ",
|
||||
"Evaluate a simple mathematical expression. Supports native JavaScript operators:",
|
||||
" ",
|
||||
@ -186,47 +195,49 @@ export const HelpTexts: IMap<string[]> = {
|
||||
" ",
|
||||
"Example:",
|
||||
" ",
|
||||
"expr 25 * 2 ** 10",
|
||||
" expr 25 * 2 ** 10",
|
||||
" ",
|
||||
"Note that letters (non-digits) are not allowed and will be removed from the input.",
|
||||
" ",
|
||||
],
|
||||
free: [
|
||||
"free",
|
||||
"Usage: free",
|
||||
" ",
|
||||
"Displays the memory usage on the current machine. Print the amount of RAM that is available on the current server as well as ",
|
||||
"how much of it is being used.",
|
||||
" ",
|
||||
],
|
||||
grow: [
|
||||
"grow",
|
||||
"",
|
||||
"Usage: grow",
|
||||
" ",
|
||||
"Spoof transactions in the current server. Increasing the money available by hacking. Requires root access.",
|
||||
"See the wiki page for hacking mechanics.",
|
||||
" ",
|
||||
],
|
||||
hack: [
|
||||
"hack",
|
||||
"Usage: hack",
|
||||
" ",
|
||||
"Attempt to hack the current server. Requires root access in order to be run. See the wiki page for hacking mechanics",
|
||||
" ",
|
||||
],
|
||||
help: [
|
||||
"help [command]",
|
||||
"Usage: help [command]",
|
||||
" ",
|
||||
"Display Terminal help information. Without arguments, 'help' prints a list of all valid Terminal commands and a brief ",
|
||||
"description of their functionality. You can also pass the name of a Terminal command as an argument to 'help' to print ",
|
||||
"more detailed information about the Terminal command. Examples: ",
|
||||
" ",
|
||||
"help alias",
|
||||
" help alias",
|
||||
" ",
|
||||
" help scan-analyze",
|
||||
" ",
|
||||
"help scan-analyze",
|
||||
],
|
||||
home: [
|
||||
"home" + "Connect to your home computer. This will work no matter what server you are currently connected to.",
|
||||
"Usage: home", " ", "Connect to your home computer. This will work no matter what server you are currently connected to.", " ",
|
||||
],
|
||||
hostname: ["hostname", " ", "Prints the hostname of the current server"],
|
||||
hostname: ["Usage: hostname", " ", "Prints the hostname of the current server", " "],
|
||||
kill: [
|
||||
"kill [script name] [args...]",
|
||||
" ",
|
||||
"kill [pid]",
|
||||
"Usage: kill [script name] [args...] or kill [pid",
|
||||
" ",
|
||||
"Kill the script specified by the script name and arguments OR by its PID.",
|
||||
" ",
|
||||
@ -235,24 +246,26 @@ export const HelpTexts: IMap<string[]> = {
|
||||
"uniquely identified by both its name and the arguments that are used to start ",
|
||||
"it. So, if a script was ran with the following arguments:",
|
||||
" ",
|
||||
"run foo.script 1 sigma-cosmetics",
|
||||
" run foo.script 1 sigma-cosmetics",
|
||||
" ",
|
||||
"Then to kill this script the same arguments would have to be used:",
|
||||
" ",
|
||||
"kill foo.script 1 sigma-cosmetics",
|
||||
" kill foo.script 1 sigma-cosmetics",
|
||||
" ",
|
||||
"If you are killing the script using its PID, then the PID argument must be numeric",
|
||||
" ",
|
||||
],
|
||||
killall: [
|
||||
"killall",
|
||||
"Usage: killall",
|
||||
" ",
|
||||
"Kills all scripts on the current server. ",
|
||||
"Note that after the 'kill' command is issued for a script, it may take a while for the script to actually stop running. ",
|
||||
"This will happen if the script is in the middle of a command such as grow() or weaken() that takes time to execute. ",
|
||||
"The script will not be stopped/killed until after that time has elapsed.",
|
||||
" ",
|
||||
],
|
||||
ls: [
|
||||
"ls [dir] [| grep pattern]",
|
||||
"Usage: ls [dir] [| grep pattern]",
|
||||
" ",
|
||||
"The ls command, with no arguments, prints all files and directories on the current server's directory to the Terminal screen. ",
|
||||
"The files will be displayed in alphabetical order. ",
|
||||
@ -265,34 +278,36 @@ export const HelpTexts: IMap<string[]> = {
|
||||
" ",
|
||||
"List all files with the '.script' extension in the current directory:",
|
||||
" ",
|
||||
"ls | grep .script",
|
||||
" ls | grep .script",
|
||||
" ",
|
||||
"List all files with the '.js' extension in the root directory:",
|
||||
" ",
|
||||
"ls / | grep .js",
|
||||
" ls / | grep .js",
|
||||
" ",
|
||||
"List all files with the word 'purchase' in the filename, in the 'scripts' directory:",
|
||||
" ",
|
||||
"ls scripts | grep purchase",
|
||||
" ls scripts | grep purchase",
|
||||
" ",
|
||||
],
|
||||
lscpu: ["lscpu", " ", "Prints the number of CPU Cores the current server has"],
|
||||
lscpu: ["Usage: lscpu", " ", "Prints the number of CPU Cores the current server has", " "],
|
||||
|
||||
mem: [
|
||||
"mem [script name] [-t num_threads]",
|
||||
"Usage: mem [script name] [-t num_threads]",
|
||||
" ",
|
||||
"Displays the amount of RAM needed to run the specified script with a single thread. The command can also be used to print ",
|
||||
"the amount of RAM needed to run a script with multiple threads using the '-t' flag. If the '-t' flag is specified, then ",
|
||||
"an argument for the number of threads must be passed in afterwards. Examples:",
|
||||
" ",
|
||||
"mem foo.script",
|
||||
" mem foo.script",
|
||||
" ",
|
||||
"mem foo.script -t 50",
|
||||
" mem foo.script -t 50",
|
||||
" ",
|
||||
"The first example above will print the amount of RAM needed to run 'foo.script' with a single thread. The second example ",
|
||||
"above will print the amount of RAM needed to run 'foo.script' with 50 threads.",
|
||||
" ",
|
||||
],
|
||||
mv: [
|
||||
"mv [src] [dest]",
|
||||
"Usage: mv [src] [dest]",
|
||||
" ",
|
||||
"Move the source file to the specified destination. This can also be used to rename files. ",
|
||||
"This command only works for scripts and text files (.txt). This command CANNOT be used to ",
|
||||
@ -302,28 +317,31 @@ export const HelpTexts: IMap<string[]> = {
|
||||
"full filepath. ",
|
||||
"Examples: ",
|
||||
" ",
|
||||
"mv hacking-controller.script scripts/hacking-controller.script",
|
||||
" mv hacking-controller.script scripts/hacking-controller.script",
|
||||
" ",
|
||||
" mv myScript.js myOldScript.js",
|
||||
" ",
|
||||
"mv myScript.js myOldScript.js",
|
||||
],
|
||||
nano: [
|
||||
"nano [file ...]",
|
||||
"Usage: nano [file ...]",
|
||||
" ",
|
||||
"Opens up the specified file(s) in the Text Editor. Only scripts (.script) or text files (.txt) can be ",
|
||||
"edited using the Text Editor. If the file does not already exist, then a new, empty one ",
|
||||
"will be created",
|
||||
" ",
|
||||
],
|
||||
ps: ["ps", " ", "Prints all scripts that are running on the current server"],
|
||||
ps: ["Usage: ps", " ", "Prints all scripts that are running on the current server", " "],
|
||||
|
||||
rm: [
|
||||
"rm [file]",
|
||||
"Usage: rm [file]",
|
||||
" ",
|
||||
"Removes the specified file from the current server. A file can be a script, a program, or a message file. ",
|
||||
" ",
|
||||
"WARNING: This is permanent and cannot be undone",
|
||||
" ",
|
||||
],
|
||||
run: [
|
||||
"run [file name] [-t] [num threads] [args...]",
|
||||
"Usage: run [file name] [-t] [num threads] [args...]",
|
||||
" ",
|
||||
"Execute a program, script or coding contract.",
|
||||
" ",
|
||||
@ -338,13 +356,14 @@ export const HelpTexts: IMap<string[]> = {
|
||||
" ",
|
||||
],
|
||||
scan: [
|
||||
"scan",
|
||||
"Usage: scan",
|
||||
" ",
|
||||
"Prints all immediately-available network connection. This will print a list of all servers that you can currently connect ",
|
||||
"to using the 'connect' Terminal command.",
|
||||
" ",
|
||||
],
|
||||
"scan-analyze": [
|
||||
"scan-analyze [depth] [-a]",
|
||||
"Usage: scan-analyze [depth] [-a]",
|
||||
" ",
|
||||
"Prints detailed information about all servers up to [depth] nodes away on the network. Calling ",
|
||||
"'scan-analyze 1' will display information for the same servers that are shown by the 'scan' Terminal ",
|
||||
@ -360,71 +379,77 @@ export const HelpTexts: IMap<string[]> = {
|
||||
" ",
|
||||
"By default, this command will not display servers that you have purchased. However, you can pass in the ",
|
||||
"-a flag at the end of the command if you would like to enable that.",
|
||||
" ",
|
||||
],
|
||||
scp: [
|
||||
"scp [filename ...] [target server]",
|
||||
"Usage: scp [filename ...] [target server]",
|
||||
" ",
|
||||
"Copies the specified file(s) from the current server to the target server. ",
|
||||
"This command only works for script files (.script or .js extension), literature files (.lit extension), ",
|
||||
"and text files (.txt extension). ",
|
||||
"The second argument passed in must be the hostname or IP of the target server. Examples:",
|
||||
" ",
|
||||
"scp foo.script n00dles",
|
||||
" scp foo.script n00dles",
|
||||
" ",
|
||||
"scp foo.script bar.script n00dles",
|
||||
" scp foo.script bar.script n00dles",
|
||||
" ",
|
||||
],
|
||||
sudov: ["sudov", " ", "Prints whether or not you have root access to the current machine"],
|
||||
sudov: ["Usage: sudov", " ", "Prints whether or not you have root access to the current machine", " "],
|
||||
|
||||
tail: [
|
||||
"tail [script name] [args...]",
|
||||
"Usage: tail [script name] [args...]",
|
||||
" ",
|
||||
"Displays dynamic logs for the script specified by the script name and arguments. Each argument must be separated ",
|
||||
"by a space. Remember that a running script is uniquely identified by both its name and the arguments that were used ",
|
||||
"to run it. So, if a script was ran with the following arguments: ",
|
||||
" ",
|
||||
"run foo.script 10 50000",
|
||||
" run foo.script 10 50000",
|
||||
" ",
|
||||
"Then in order to check its logs with 'tail' the same arguments must be used: ",
|
||||
" ",
|
||||
"tail foo.script 10 50000",
|
||||
" tail foo.script 10 50000",
|
||||
" ",
|
||||
],
|
||||
top: [
|
||||
"top",
|
||||
"Usage: top",
|
||||
" ",
|
||||
"Prints a list of all scripts running on the current server as well as their thread count and how much ",
|
||||
"RAM they are using in total.",
|
||||
" ",
|
||||
],
|
||||
unalias: [
|
||||
"unalias [alias name]",
|
||||
"Usage: unalias [alias name]",
|
||||
" ",
|
||||
"Deletes the specified alias. Note that the double quotation marks are required. ",
|
||||
" ",
|
||||
"As an example, if an alias was declared using:",
|
||||
" ",
|
||||
'alias r="run"',
|
||||
' alias r="run"',
|
||||
" ",
|
||||
"Then it could be removed using:",
|
||||
" ",
|
||||
"unalias r",
|
||||
" unalias r",
|
||||
" ",
|
||||
"It is not necessary to differentiate between global and non-global aliases when using 'unalias'",
|
||||
" ",
|
||||
],
|
||||
vim: [
|
||||
"vim [file ...]",
|
||||
"Usage: vim [file ...]",
|
||||
" ",
|
||||
"Opens up the specified file(s) in the Text Editor in vim mode. Only scripts (.script) or text files (.txt) can be ",
|
||||
"edited using the Text Editor. If the file does not already exist, then a new, empty one ",
|
||||
"will be created",
|
||||
" ",
|
||||
],
|
||||
weaken: [
|
||||
"weaken",
|
||||
"",
|
||||
"Usage: weaken",
|
||||
" ",
|
||||
"Reduces the security level of the current server. Decreasing the time it takes for all operations on this server.",
|
||||
"Requires root access. See the wiki page for hacking mechanics.",
|
||||
" ",
|
||||
],
|
||||
wget: [
|
||||
"wget [url] [target file]",
|
||||
"Usage: wget [url] [target file]",
|
||||
" ",
|
||||
"Retrieves data from a URL and downloads it to a file on the current server. The data can only ",
|
||||
"be downloaded to a script (.script, .ns, .js) or a text file (.txt). If the file already exists, ",
|
||||
@ -433,6 +458,7 @@ export const HelpTexts: IMap<string[]> = {
|
||||
"Note that it will not be possible to download data from many websites because they do not allow ",
|
||||
"cross-origin resource sharing (CORS). Example:",
|
||||
" ",
|
||||
"wget https://raw.githubusercontent.com/danielyxie/bitburner/master/README.md game_readme.txt",
|
||||
" wget https://raw.githubusercontent.com/danielyxie/bitburner/master/README.md game_readme.txt",
|
||||
" ",
|
||||
],
|
||||
};
|
||||
|
@ -95,20 +95,42 @@ export function TerminalRoot({ terminal, router, player }: IProps): React.ReactE
|
||||
setKey((key) => key + 1);
|
||||
}
|
||||
|
||||
useEffect(() => TerminalEvents.subscribe(_.debounce(async () => rerender(), 25, { maxWait: 50 })), []);
|
||||
useEffect(() => TerminalClearEvents.subscribe(_.debounce(async () => clear(), 25, { maxWait: 50 })), []);
|
||||
useEffect(() => {
|
||||
const debounced = _.debounce(async () => rerender(), 25, { maxWait: 50 });
|
||||
const unsubscribe = TerminalEvents.subscribe(debounced);
|
||||
return () => {
|
||||
debounced.cancel();
|
||||
unsubscribe();
|
||||
}
|
||||
}, []);
|
||||
|
||||
function doScroll(): void {
|
||||
useEffect(() => {
|
||||
const debounced = _.debounce(async () => clear(), 25, { maxWait: 50 });
|
||||
const unsubscribe = TerminalClearEvents.subscribe(debounced);
|
||||
return () => {
|
||||
debounced.cancel();
|
||||
unsubscribe();
|
||||
}
|
||||
}, []);
|
||||
|
||||
function doScroll(): number | undefined {
|
||||
const hook = scrollHook.current;
|
||||
if (hook !== null) {
|
||||
setTimeout(() => hook.scrollIntoView(true), 50);
|
||||
return window.setTimeout(() => hook.scrollIntoView(true), 50);
|
||||
}
|
||||
}
|
||||
|
||||
doScroll();
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(doScroll, 50);
|
||||
let scrollId: number;
|
||||
const id = setTimeout(() => {
|
||||
scrollId = doScroll() ?? 0;
|
||||
}, 50);
|
||||
return () => {
|
||||
clearTimeout(id);
|
||||
clearTimeout(scrollId);
|
||||
}
|
||||
}, []);
|
||||
|
||||
function lineClass(s: string): string {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { cloneDeep, isEqual } from "lodash";
|
||||
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../IEngine";
|
||||
import { ITerminal } from "../Terminal/ITerminal";
|
||||
@ -86,11 +86,6 @@ interface IProps {
|
||||
engine: IEngine;
|
||||
}
|
||||
|
||||
interface PageHistoryEntry {
|
||||
page: Page;
|
||||
args: any[];
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
@ -110,15 +105,6 @@ export let Router: IRouter = {
|
||||
page: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
previousPage: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
clearHistory: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toPreviousPage: (): boolean => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toActiveScripts: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
@ -217,9 +203,7 @@ function determineStartPage(player: IPlayer): Page {
|
||||
export function GameRoot({ player, engine, terminal }: IProps): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
const [{ files, vim }, setEditorOptions] = useState({ files: {}, vim: false });
|
||||
const startPage = determineStartPage(player);
|
||||
const [page, setPage] = useState(startPage);
|
||||
const [pageHistory, setPageHistory] = useState<PageHistoryEntry[]>([{ page: startPage, args: [] }]);
|
||||
const [page, setPage] = useState(determineStartPage(player));
|
||||
const setRerender = useState(0)[1];
|
||||
const [faction, setFaction] = useState<Faction>(
|
||||
player.currentWorkFactionName ? Factions[player.currentWorkFactionName] : (undefined as unknown as Faction),
|
||||
@ -250,136 +234,70 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
setTimeout(() => htmlLocation.reload(), 2000);
|
||||
}
|
||||
|
||||
function setCurrentPage(page: Page, ...args: any): void {
|
||||
const newPage = { page, args: cloneDeep(args) };
|
||||
const previousPage = pageHistory[0];
|
||||
const isDifferentThenPrevious = page !== previousPage?.page || !isEqual([...args], previousPage?.args);
|
||||
if (isDifferentThenPrevious) {
|
||||
const history = [
|
||||
newPage,
|
||||
...pageHistory
|
||||
].slice(0, 20);
|
||||
setPageHistory(history)
|
||||
}
|
||||
setPage(page)
|
||||
}
|
||||
|
||||
function goBack(fallback: (...args: any[]) => void): void {
|
||||
const [ , previousPage ] = pageHistory;
|
||||
if (previousPage) {
|
||||
const handler = pageToRouterMap[previousPage?.page];
|
||||
handler(...previousPage.args);
|
||||
} else {
|
||||
if (fallback) fallback();
|
||||
}
|
||||
const [ , ...history] = pageHistory;
|
||||
setPageHistory(cloneDeep(history));
|
||||
}
|
||||
|
||||
const pageToRouterMap: { [key: number] : (...args: any[]) => void } = {
|
||||
[Page.ActiveScripts]: Router.toActiveScripts,
|
||||
[Page.Augmentations]: Router.toAugmentations,
|
||||
[Page.Bladeburner]: Router.toBladeburner,
|
||||
[Page.Stats]: Router.toStats,
|
||||
[Page.Corporation]: Router.toCorporation,
|
||||
[Page.CreateProgram]: Router.toCreateProgram,
|
||||
[Page.DevMenu]: Router.toDevMenu,
|
||||
[Page.Faction]: Router.toFaction,
|
||||
[Page.Factions]: Router.toFactions,
|
||||
[Page.Options]: Router.toGameOptions,
|
||||
[Page.Gang]: Router.toGang,
|
||||
[Page.Hacknet]: Router.toHacknetNodes,
|
||||
[Page.Milestones]: Router.toMilestones,
|
||||
[Page.Resleeves]: Router.toResleeves,
|
||||
[Page.ScriptEditor]: Router.toScriptEditor,
|
||||
[Page.Sleeves]: Router.toSleeves,
|
||||
[Page.StockMarket]: Router.toStockMarket,
|
||||
[Page.Terminal]: Router.toTerminal,
|
||||
[Page.Tutorial]: Router.toTutorial,
|
||||
[Page.Job]: Router.toJob,
|
||||
[Page.City]: Router.toCity,
|
||||
[Page.Travel]: Router.toTravel,
|
||||
[Page.BitVerse]: Router.toBitVerse,
|
||||
[Page.Infiltration]: Router.toInfiltration,
|
||||
[Page.Work]: Router.toWork,
|
||||
[Page.BladeburnerCinematic]: Router.toBladeburnerCinematic,
|
||||
[Page.Location]: Router.toLocation,
|
||||
[Page.StaneksGift]: Router.toStaneksGift,
|
||||
[Page.Achievements]: Router.toAchievements,
|
||||
}
|
||||
|
||||
Router = {
|
||||
page: () => page,
|
||||
previousPage: () => {
|
||||
const [ , previousPage] = pageHistory;
|
||||
return previousPage?.page ?? -1;
|
||||
},
|
||||
clearHistory: () => setPageHistory([]),
|
||||
toPreviousPage: goBack,
|
||||
toActiveScripts: () => setCurrentPage(Page.ActiveScripts),
|
||||
toAugmentations: () => setCurrentPage(Page.Augmentations),
|
||||
toBladeburner: () => setCurrentPage(Page.Bladeburner),
|
||||
toStats: () => setCurrentPage(Page.Stats),
|
||||
toCorporation: () => setCurrentPage(Page.Corporation),
|
||||
toCreateProgram: () => setCurrentPage(Page.CreateProgram),
|
||||
toDevMenu: () => setCurrentPage(Page.DevMenu),
|
||||
toActiveScripts: () => setPage(Page.ActiveScripts),
|
||||
toAugmentations: () => setPage(Page.Augmentations),
|
||||
toBladeburner: () => setPage(Page.Bladeburner),
|
||||
toStats: () => setPage(Page.Stats),
|
||||
toCorporation: () => setPage(Page.Corporation),
|
||||
toCreateProgram: () => setPage(Page.CreateProgram),
|
||||
toDevMenu: () => setPage(Page.DevMenu),
|
||||
toFaction: (faction?: Faction) => {
|
||||
setCurrentPage(Page.Faction, faction);
|
||||
setPage(Page.Faction);
|
||||
if (faction) setFaction(faction);
|
||||
},
|
||||
toFactions: () => setCurrentPage(Page.Factions),
|
||||
toGameOptions: () => setCurrentPage(Page.Options),
|
||||
toGang: () => setCurrentPage(Page.Gang),
|
||||
toHacknetNodes: () => setCurrentPage(Page.Hacknet),
|
||||
toMilestones: () => setCurrentPage(Page.Milestones),
|
||||
toResleeves: () => setCurrentPage(Page.Resleeves),
|
||||
toFactions: () => setPage(Page.Factions),
|
||||
toGameOptions: () => setPage(Page.Options),
|
||||
toGang: () => setPage(Page.Gang),
|
||||
toHacknetNodes: () => setPage(Page.Hacknet),
|
||||
toMilestones: () => setPage(Page.Milestones),
|
||||
toResleeves: () => setPage(Page.Resleeves),
|
||||
toScriptEditor: (files: Record<string, string>, options?: ScriptEditorRouteOptions) => {
|
||||
setEditorOptions({
|
||||
files,
|
||||
vim: !!options?.vim,
|
||||
});
|
||||
setCurrentPage(Page.ScriptEditor, files, options);
|
||||
setPage(Page.ScriptEditor);
|
||||
},
|
||||
toSleeves: () => setCurrentPage(Page.Sleeves),
|
||||
toStockMarket: () => setCurrentPage(Page.StockMarket),
|
||||
toTerminal: () => setCurrentPage(Page.Terminal),
|
||||
toTutorial: () => setCurrentPage(Page.Tutorial),
|
||||
toSleeves: () => setPage(Page.Sleeves),
|
||||
toStockMarket: () => setPage(Page.StockMarket),
|
||||
toTerminal: () => setPage(Page.Terminal),
|
||||
toTutorial: () => setPage(Page.Tutorial),
|
||||
toJob: () => {
|
||||
setLocation(Locations[player.companyName]);
|
||||
setCurrentPage(Page.Job);
|
||||
setPage(Page.Job);
|
||||
},
|
||||
toCity: () => {
|
||||
setCurrentPage(Page.City);
|
||||
setPage(Page.City);
|
||||
},
|
||||
toTravel: () => {
|
||||
player.gotoLocation(LocationName.TravelAgency);
|
||||
setCurrentPage(Page.Travel);
|
||||
setPage(Page.Travel);
|
||||
},
|
||||
toBitVerse: (flume: boolean, quick: boolean) => {
|
||||
setFlume(flume);
|
||||
setQuick(quick);
|
||||
setCurrentPage(Page.BitVerse, flume, quick);
|
||||
setPage(Page.BitVerse);
|
||||
},
|
||||
toInfiltration: (location: Location) => {
|
||||
setLocation(location);
|
||||
setCurrentPage(Page.Infiltration, location);
|
||||
},
|
||||
toWork: () => {
|
||||
setCurrentPage(Page.Work);
|
||||
setPage(Page.Infiltration);
|
||||
},
|
||||
toWork: () => setPage(Page.Work),
|
||||
toBladeburnerCinematic: () => {
|
||||
setCurrentPage(Page.BladeburnerCinematic);
|
||||
setPage(Page.BladeburnerCinematic);
|
||||
setCinematicText(cinematicText);
|
||||
},
|
||||
toLocation: (location: Location) => {
|
||||
setLocation(location);
|
||||
setCurrentPage(Page.Location, location);
|
||||
setPage(Page.Location);
|
||||
},
|
||||
toStaneksGift: () => {
|
||||
setCurrentPage(Page.StaneksGift);
|
||||
setPage(Page.StaneksGift);
|
||||
},
|
||||
toAchievements: () => {
|
||||
setCurrentPage(Page.Achievements);
|
||||
setPage(Page.Achievements);
|
||||
},
|
||||
};
|
||||
|
||||
@ -582,11 +500,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
<SnackbarProvider>
|
||||
<Overview mode={ITutorial.isRunning ? "tutorial" : "overview"}>
|
||||
{!ITutorial.isRunning ? (
|
||||
<CharacterOverview
|
||||
save={() => saveObject.saveGame()}
|
||||
killScripts={killAllScripts}
|
||||
router={Router}
|
||||
allowBackButton={withSidebar} />
|
||||
<CharacterOverview save={() => saveObject.saveGame()} killScripts={killAllScripts} />
|
||||
) : (
|
||||
<InteractiveTutorialRoot />
|
||||
)}
|
||||
|
@ -17,22 +17,19 @@ import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import SaveIcon from "@mui/icons-material/Save";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import ClearAllIcon from "@mui/icons-material/ClearAll";
|
||||
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { use } from "../Context";
|
||||
import { StatsProgressOverviewCell } from "./StatsProgressBar";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { IRouter, Page } from "../Router";
|
||||
|
||||
import { Box, Tooltip } from "@mui/material";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
|
||||
interface IProps {
|
||||
save: () => void;
|
||||
killScripts: () => void;
|
||||
router: IRouter;
|
||||
allowBackButton: boolean;
|
||||
}
|
||||
|
||||
function Intelligence(): React.ReactElement {
|
||||
@ -239,7 +236,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
|
||||
export { useStyles as characterOverviewStyles };
|
||||
|
||||
export function CharacterOverview({ save, killScripts, router, allowBackButton }: IProps): React.ReactElement {
|
||||
export function CharacterOverview({ save, killScripts }: IProps): React.ReactElement {
|
||||
const [killOpen, setKillOpen] = useState(false);
|
||||
const player = use.Player();
|
||||
|
||||
@ -278,9 +275,6 @@ export function CharacterOverview({ save, killScripts, router, allowBackButton }
|
||||
player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier,
|
||||
);
|
||||
|
||||
const previousPageName =
|
||||
router.previousPage() < 0 ? "" : Page[router.previousPage() ?? 0].replace(/([a-z])([A-Z])/g, "$1 $2");
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table sx={{ display: "block", m: 1 }}>
|
||||
@ -464,13 +458,6 @@ export function CharacterOverview({ save, killScripts, router, allowBackButton }
|
||||
<SaveIcon color={Settings.AutosaveInterval !== 0 ? "primary" : "error"} />
|
||||
</Tooltip>
|
||||
</IconButton>
|
||||
{allowBackButton && (
|
||||
<IconButton disabled={!previousPageName} onClick={() => router.toPreviousPage()}>
|
||||
<Tooltip title={previousPageName ? `Go back to "${previousPageName}"` : ""}>
|
||||
<ArrowBackIcon />
|
||||
</Tooltip>
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
<Box sx={{ display: "flex", flex: 1, justifyContent: "flex-end", alignItems: "center" }}>
|
||||
<IconButton onClick={() => setKillOpen(true)}>
|
||||
|
@ -25,20 +25,22 @@ export function CorruptableText(props: IProps): JSX.Element {
|
||||
|
||||
useEffect(() => {
|
||||
let counter = 5;
|
||||
const id = setInterval(() => {
|
||||
const timers: number[] = [];
|
||||
const intervalId = setInterval(() => {
|
||||
counter--;
|
||||
if (counter > 0) return;
|
||||
counter = Math.random() * 5;
|
||||
const index = Math.random() * content.length;
|
||||
const letter = content.charAt(index);
|
||||
setContent((content) => replace(content, index, randomize(letter)));
|
||||
setTimeout(() => {
|
||||
timers.push(window.setTimeout(() => {
|
||||
setContent((content) => replace(content, index, letter));
|
||||
}, 500);
|
||||
}, 500));
|
||||
}, 20);
|
||||
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
clearInterval(intervalId);
|
||||
timers.forEach((timerId) => clearTimeout(timerId));
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
@ -7,12 +7,13 @@ import Typography from "@mui/material/Typography";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import ReplyIcon from "@mui/icons-material/Reply";
|
||||
import SaveIcon from '@mui/icons-material/Save';
|
||||
import SaveIcon from "@mui/icons-material/Save";
|
||||
|
||||
import { ThemeEvents } from "./Theme";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { IStyleSettings, defaultStyles } from "../../Settings/Styles";
|
||||
import { defaultStyles } from "../../Settings/Styles";
|
||||
import { Tooltip } from "@mui/material";
|
||||
import { IStyleSettings } from "../../ScriptEditor/NetscriptDefinitions";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
@ -25,16 +26,16 @@ interface FontFamilyProps {
|
||||
refreshId: number;
|
||||
}
|
||||
|
||||
function FontFamilyField({ value, onChange, refreshId } : FontFamilyProps): React.ReactElement {
|
||||
function FontFamilyField({ value, onChange, refreshId }: FontFamilyProps): React.ReactElement {
|
||||
const [errorText, setErrorText] = useState<string | undefined>();
|
||||
const [fontFamily, setFontFamily] = useState<React.CSSProperties["fontFamily"]>(value);
|
||||
|
||||
function update(newValue: React.CSSProperties["fontFamily"]): void {
|
||||
setFontFamily(newValue);
|
||||
if (!newValue) {
|
||||
setErrorText('Must have a value');
|
||||
setErrorText("Must have a value");
|
||||
} else {
|
||||
setErrorText('');
|
||||
setErrorText("");
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +56,7 @@ function FontFamilyField({ value, onChange, refreshId } : FontFamilyProps): Reac
|
||||
onChange={onTextChange}
|
||||
fullWidth
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
interface LineHeightProps {
|
||||
@ -64,18 +65,18 @@ interface LineHeightProps {
|
||||
refreshId: number;
|
||||
}
|
||||
|
||||
function LineHeightField({ value, onChange, refreshId } : LineHeightProps): React.ReactElement {
|
||||
function LineHeightField({ value, onChange, refreshId }: LineHeightProps): React.ReactElement {
|
||||
const [errorText, setErrorText] = useState<string | undefined>();
|
||||
const [lineHeight, setLineHeight] = useState<React.CSSProperties["lineHeight"]>(value);
|
||||
|
||||
function update(newValue: React.CSSProperties["lineHeight"]): void {
|
||||
setLineHeight(newValue);
|
||||
if (!newValue) {
|
||||
setErrorText('Must have a value');
|
||||
setErrorText("Must have a value");
|
||||
} else if (isNaN(Number(newValue))) {
|
||||
setErrorText('Must be a number');
|
||||
setErrorText("Must be a number");
|
||||
} else {
|
||||
setErrorText('');
|
||||
setErrorText("");
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +96,7 @@ function LineHeightField({ value, onChange, refreshId } : LineHeightProps): Reac
|
||||
helperText={errorText}
|
||||
onChange={onTextChange}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function StyleEditorModal(props: IProps): React.ReactElement {
|
||||
@ -115,7 +116,7 @@ export function StyleEditorModal(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
function setDefaults(): void {
|
||||
const styles = {...defaultStyles}
|
||||
const styles = { ...defaultStyles };
|
||||
setCustomStyle(styles);
|
||||
persistToSettings(styles);
|
||||
setRefreshId(refreshId + 1);
|
||||
@ -132,16 +133,23 @@ export function StyleEditorModal(props: IProps): React.ReactElement {
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography variant="h6">Styles Editor</Typography>
|
||||
<Typography>
|
||||
WARNING: Changing styles <strong>may mess up</strong> the interface. Drastic changes are <strong>NOT recommended</strong>.
|
||||
WARNING: Changing styles <strong>may mess up</strong> the interface. Drastic changes are{" "}
|
||||
<strong>NOT recommended</strong>.
|
||||
</Typography>
|
||||
<Paper sx={{ p: 2, my: 2 }}>
|
||||
<FontFamilyField value={customStyle.fontFamily} refreshId={refreshId}
|
||||
onChange={(value, error) => update({ ...customStyle, fontFamily: value }, error)} />
|
||||
<FontFamilyField
|
||||
value={customStyle.fontFamily}
|
||||
refreshId={refreshId}
|
||||
onChange={(value, error) => update({ ...customStyle, fontFamily: value as any }, error)}
|
||||
/>
|
||||
<br />
|
||||
<LineHeightField value={customStyle.lineHeight} refreshId={refreshId}
|
||||
onChange={(value, error) => update({ ...customStyle, lineHeight: value }, error)} />
|
||||
<LineHeightField
|
||||
value={customStyle.lineHeight}
|
||||
refreshId={refreshId}
|
||||
onChange={(value, error) => update({ ...customStyle, lineHeight: value as any }, error)}
|
||||
/>
|
||||
<br />
|
||||
<ButtonGroup sx={{ my: 1}}>
|
||||
<ButtonGroup sx={{ my: 1 }}>
|
||||
<Button onClick={setDefaults} startIcon={<ReplyIcon />} color="secondary" variant="outlined">
|
||||
Revert to Defaults
|
||||
</Button>
|
||||
|
@ -53,9 +53,6 @@ export interface IRouter {
|
||||
// toRedPill(): void;
|
||||
// toworkInProgress(): void;
|
||||
page(): Page;
|
||||
previousPage(): Page;
|
||||
clearHistory(): void;
|
||||
toPreviousPage(fallback?: (...args: any[]) => void): void;
|
||||
toActiveScripts(): void;
|
||||
toAugmentations(): void;
|
||||
toBitVerse(flume: boolean, quick: boolean): void;
|
||||
|
@ -36,12 +36,12 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
const faction = Factions[player.currentWorkFactionName];
|
||||
if (player.workType == CONSTANTS.WorkTypeFaction) {
|
||||
function cancel(): void {
|
||||
router.toFaction(faction);
|
||||
player.finishFactionWork(true);
|
||||
router.toPreviousPage(() => router.toFaction(faction));
|
||||
}
|
||||
function unfocus(): void {
|
||||
router.toFaction(faction);
|
||||
player.stopFocusing();
|
||||
router.toPreviousPage(() => router.toFaction(faction));
|
||||
}
|
||||
return (
|
||||
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
|
||||
@ -120,12 +120,13 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
if (player.className !== "") {
|
||||
function cancel(): void {
|
||||
player.finishClass(true);
|
||||
router.toPreviousPage(() => router.toCity());
|
||||
router.toCity();
|
||||
}
|
||||
|
||||
function unfocus(): void {
|
||||
router.toFaction(faction);
|
||||
router.toCity();
|
||||
player.stopFocusing();
|
||||
router.toPreviousPage(() => router.toCity());
|
||||
}
|
||||
|
||||
let stopText = "";
|
||||
@ -211,11 +212,11 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
|
||||
function cancel(): void {
|
||||
player.finishWork(true);
|
||||
router.toPreviousPage(() => router.toJob());
|
||||
router.toJob();
|
||||
}
|
||||
function unfocus(): void {
|
||||
player.stopFocusing();
|
||||
router.toPreviousPage(() => router.toJob());
|
||||
router.toJob();
|
||||
}
|
||||
|
||||
const position = player.jobs[player.companyName];
|
||||
@ -303,11 +304,11 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
if (player.workType == CONSTANTS.WorkTypeCompanyPartTime) {
|
||||
function cancel(): void {
|
||||
player.finishWorkPartTime(true);
|
||||
router.toPreviousPage(() => router.toJob());
|
||||
router.toJob();
|
||||
}
|
||||
function unfocus(): void {
|
||||
player.stopFocusing();
|
||||
router.toPreviousPage(() => router.toJob());
|
||||
router.toJob();
|
||||
}
|
||||
const comp = Companies[player.companyName];
|
||||
let companyRep = 0;
|
||||
@ -439,11 +440,11 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
if (player.createProgramName !== "") {
|
||||
function cancel(): void {
|
||||
player.finishCreateProgramWork(true);
|
||||
router.toPreviousPage(() => router.toTerminal());
|
||||
router.toTerminal();
|
||||
}
|
||||
function unfocus(): void {
|
||||
router.toTerminal();
|
||||
player.stopFocusing();
|
||||
router.toPreviousPage(() => router.toTerminal());
|
||||
}
|
||||
return (
|
||||
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
|
||||
|
Loading…
Reference in New Issue
Block a user