mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-19 22:23:51 +01:00
Merge branch 'dev' of https://github.com/Undeemiss/bitburner into join-stanek-api
This commit is contained in:
commit
707e643c7d
5
.github/PULL_REQUEST_TEMPLATE
vendored
5
.github/PULL_REQUEST_TEMPLATE
vendored
@ -9,6 +9,11 @@ SECTION is something like "API", "UI", "MISC", "STANEK", "CORPORATION"
|
||||
FIX #xyzw is the issue number, if any
|
||||
PLAYER DESCRIPTION is what you'd tell a non-contributor to convey what is changed.
|
||||
|
||||
# Linked issues
|
||||
|
||||
If your pull request is related to a git issue, please link it in the description using #xyz.
|
||||
If your PR should close the issue when it is merged in, use `fixes #xyz` or `closes #xyz`. It'll automate the process.
|
||||
|
||||
# Documentation
|
||||
|
||||
- DO NOT CHANGE any markdown/\*.md, these files are autogenerated from NetscriptDefinitions.d.ts and will be overwritten
|
||||
|
64
.github/workflows/validate-pr.yml
vendored
Normal file
64
.github/workflows/validate-pr.yml
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
name: Validate PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [dev]
|
||||
types: [opened, edited, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
checkTitle:
|
||||
name: Check Title
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
- name: Validate Title
|
||||
id: validate-pr-title
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
echo "Creating label (if it does not exist)"
|
||||
LABEL="validation: invalid title"
|
||||
gh --repo "${{ github.repository }}" \
|
||||
label create "$LABEL" --description "Modifications to this pull request are requested" --color D93F0B || true
|
||||
|
||||
PR_TITLE=$(\
|
||||
gh --repo "${{ github.repository }}" \
|
||||
pr view "${{ github.event.number }}" --json title --jq .title)
|
||||
echo "::set-output name=title::$PR_TITLE"
|
||||
echo "PR Title: $PR_TITLE"
|
||||
|
||||
TITLE_REGEX="^[0-9A-Z\-]*: .*$"
|
||||
PR_TITLE_VALID=$(echo "$PR_TITLE" | grep -Eq "$TITLE_REGEX" && echo "true" || echo "false")
|
||||
|
||||
if [ "$PR_TITLE_VALID" == "true" ]; then
|
||||
echo "Title is valid, removing label"
|
||||
gh --repo "${{ github.repository }}" \
|
||||
pr edit "${{ github.event.number }}" --remove-label "$LABEL" || true
|
||||
else
|
||||
echo "Invalid Title"
|
||||
ERROR_MSG="$PR_TITLE -> should match -> $TITLE_REGEX"
|
||||
echo "$ERROR_MSG"
|
||||
echo "::set-output name=invalid::true"
|
||||
echo "::set-output name=errorMessage::$ERROR_MSG"
|
||||
|
||||
touch comment.txt
|
||||
echo "## The title \`$PR_TITLE\` should match \`$TITLE_REGEX\`" >> comment.txt
|
||||
echo "" >> comment.txt
|
||||
echo "SECTION: FIX #xzyw PLAYER DESCRIPTION" >> comment.txt
|
||||
echo "" >> comment.txt
|
||||
echo 'SECTION is something like "API", "UI", "MISC", "STANEK", "CORPORATION"' >> comment.txt
|
||||
echo 'FIX #xyzw is the issue number, if any' >> comment.txt
|
||||
echo "PLAYER DESCRIPTION is what you'd tell a non-contributor to convey what is changed." >> comment.txt
|
||||
|
||||
echo "Add pr label"
|
||||
gh --repo "${{ github.repository }}" \
|
||||
pr edit "${{ github.event.number }}" --add-label "$LABEL"
|
||||
echo "And comment on the pr"
|
||||
gh --repo "${{ github.repository }}" \
|
||||
pr comment "${{ github.event.number }}" --body-file comment.txt
|
||||
fi
|
||||
- name: Flag workflow error
|
||||
if: steps.validate-pr-title.outputs.invalid == 'true'
|
||||
run: |
|
||||
echo "${{ steps.validate-pr-title.outputs.errorMessage }}"
|
||||
exit 1
|
4
dist/main.bundle.js
vendored
4
dist/main.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/main.bundle.js.map
vendored
2
dist/main.bundle.js.map
vendored
File diff suppressed because one or more lines are too long
66
dist/vendor.bundle.js
vendored
66
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
@ -113,7 +113,7 @@ export const achievements: IMap<Achievement> = {
|
||||
},
|
||||
THE_COVENANT: {
|
||||
...achievementData["THE_COVENANT"],
|
||||
Icon: FactionNames.TheCovenant.toLowerCase(),
|
||||
Icon: FactionNames.TheCovenant.toLowerCase().replace(/ /g, ""),
|
||||
Condition: () => Player.factions.includes(FactionNames.TheCovenant),
|
||||
},
|
||||
[FactionNames.Illuminati.toUpperCase()]: {
|
||||
|
@ -1907,7 +1907,8 @@ export function initNeuroFluxGovernor(): Augmentation {
|
||||
stats: (
|
||||
<>
|
||||
This special augmentation can be leveled up infinitely. Each level of this augmentation increases MOST
|
||||
multipliers by 1% (+{donationBonus * 100}% boosted by real life blood donations), stacking multiplicatively.
|
||||
multipliers by 1% (+{(donationBonus * 100).toFixed(6)}% boosted by real life blood donations), stacking
|
||||
multiplicatively.
|
||||
</>
|
||||
),
|
||||
hacking_chance_mult: 1.01 + donationBonus,
|
||||
|
@ -172,7 +172,7 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip title={<Typography>It's always a good idea to backup/export your save!</Typography>}>
|
||||
<Button sx={{ width: "100%" }} onClick={doExport} color="error">
|
||||
<Button sx={{ width: "100%", color: Settings.theme.successlight }} onClick={doExport}>
|
||||
Backup Save {exportBonusStr()}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
@ -118,7 +118,7 @@ export const CONSTANTS: {
|
||||
LatestUpdate: string;
|
||||
} = {
|
||||
VersionString: "1.6.4",
|
||||
VersionNumber: 14,
|
||||
VersionNumber: 15,
|
||||
|
||||
// Speed (in ms) at which the main loop is updated
|
||||
_idleSpeed: 200,
|
||||
@ -287,7 +287,7 @@ export const CONSTANTS: {
|
||||
// BitNode/Source-File related stuff
|
||||
TotalNumBitNodes: 24,
|
||||
|
||||
Donations: 2,
|
||||
Donations: 4,
|
||||
|
||||
LatestUpdate: `
|
||||
v1.6.3 - 2022-04-01 Few stanek fixes
|
||||
|
6
src/GameOptions/GameOptionsTab.ts
Normal file
6
src/GameOptions/GameOptionsTab.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export enum GameOptionsTab {
|
||||
SYSTEM,
|
||||
INTERFACE,
|
||||
GAMEPLAY,
|
||||
MISC,
|
||||
}
|
356
src/GameOptions/ui/CurrentOptionsPage.tsx
Normal file
356
src/GameOptions/ui/CurrentOptionsPage.tsx
Normal file
@ -0,0 +1,356 @@
|
||||
import { MenuItem, Select, SelectChangeEvent, TextField, Tooltip, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { OptionSwitch } from "../../ui/React/OptionSwitch";
|
||||
import { formatTime } from "../../utils/helpers/formatTime";
|
||||
import { GameOptionsTab } from "../GameOptionsTab";
|
||||
import { GameOptionsPage } from "./GameOptionsPage";
|
||||
import { OptionsSlider } from "./OptionsSlider";
|
||||
|
||||
interface IProps {
|
||||
currentTab: GameOptionsTab;
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
|
||||
const [execTime, setExecTime] = useState(Settings.CodeInstructionRunTime);
|
||||
const [recentScriptsSize, setRecentScriptsSize] = useState(Settings.MaxRecentScriptsCapacity);
|
||||
const [logSize, setLogSize] = useState(Settings.MaxLogCapacity);
|
||||
const [portSize, setPortSize] = useState(Settings.MaxPortCapacity);
|
||||
const [terminalSize, setTerminalSize] = useState(Settings.MaxTerminalCapacity);
|
||||
const [autosaveInterval, setAutosaveInterval] = useState(Settings.AutosaveInterval);
|
||||
const [timestampFormat, setTimestampFormat] = useState(Settings.TimestampsFormat);
|
||||
const [locale, setLocale] = useState(Settings.Locale);
|
||||
|
||||
function handleExecTimeChange(event: any, newValue: number | number[]): void {
|
||||
setExecTime(newValue as number);
|
||||
Settings.CodeInstructionRunTime = newValue as number;
|
||||
}
|
||||
|
||||
function handleRecentScriptsSizeChange(event: any, newValue: number | number[]): void {
|
||||
setRecentScriptsSize(newValue as number);
|
||||
Settings.MaxRecentScriptsCapacity = newValue as number;
|
||||
}
|
||||
|
||||
function handleLogSizeChange(event: any, newValue: number | number[]): void {
|
||||
setLogSize(newValue as number);
|
||||
Settings.MaxLogCapacity = newValue as number;
|
||||
}
|
||||
|
||||
function handlePortSizeChange(event: any, newValue: number | number[]): void {
|
||||
setPortSize(newValue as number);
|
||||
Settings.MaxPortCapacity = newValue as number;
|
||||
}
|
||||
|
||||
function handleTerminalSizeChange(event: any, newValue: number | number[]): void {
|
||||
setTerminalSize(newValue as number);
|
||||
Settings.MaxTerminalCapacity = newValue as number;
|
||||
}
|
||||
|
||||
function handleAutosaveIntervalChange(event: any, newValue: number | number[]): void {
|
||||
setAutosaveInterval(newValue as number);
|
||||
Settings.AutosaveInterval = newValue as number;
|
||||
}
|
||||
|
||||
function handleLocaleChange(event: SelectChangeEvent<string>): void {
|
||||
setLocale(event.target.value as string);
|
||||
Settings.Locale = event.target.value as string;
|
||||
}
|
||||
|
||||
function handleTimestampFormatChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setTimestampFormat(event.target.value);
|
||||
Settings.TimestampsFormat = event.target.value;
|
||||
}
|
||||
|
||||
const pages = {
|
||||
[GameOptionsTab.SYSTEM]: (
|
||||
<GameOptionsPage title="System">
|
||||
{/* Wrap in a React fragment to prevent the sliders from breaking as list items */}
|
||||
<>
|
||||
<OptionsSlider
|
||||
label=".script exec time (ms)"
|
||||
value={execTime}
|
||||
callback={handleExecTimeChange}
|
||||
step={1}
|
||||
min={5}
|
||||
max={100}
|
||||
tooltip={
|
||||
<>
|
||||
The minimum number of milliseconds it takes to execute an operation in Netscript. Setting this too low
|
||||
can result in poor performance if you have many scripts running.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Recently killed scripts size"
|
||||
value={recentScriptsSize}
|
||||
callback={handleRecentScriptsSizeChange}
|
||||
step={25}
|
||||
min={0}
|
||||
max={500}
|
||||
tooltip={
|
||||
<>
|
||||
The maximum number of lines a script's logs can hold. Setting this too high can cause the game to use a
|
||||
lot of memory if you have many scripts running.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Netscript log size"
|
||||
value={logSize}
|
||||
callback={handleLogSizeChange}
|
||||
step={20}
|
||||
min={20}
|
||||
max={500}
|
||||
tooltip={
|
||||
<>
|
||||
The maximum number of lines a script's logs can hold. Setting this too high can cause the game to use a
|
||||
lot of memory if you have many scripts running.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Netscript port size"
|
||||
value={portSize}
|
||||
callback={handlePortSizeChange}
|
||||
step={1}
|
||||
min={20}
|
||||
max={100}
|
||||
tooltip={
|
||||
<>
|
||||
The maximum number of entries that can be written to a port using Netscript's write() function. Setting
|
||||
this too high can cause the game to use a lot of memory.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Terminal capacity"
|
||||
value={terminalSize}
|
||||
callback={handleTerminalSizeChange}
|
||||
step={50}
|
||||
min={50}
|
||||
max={500}
|
||||
tooltip={
|
||||
<>
|
||||
The maximum number of entries that can be written to the terminal. Setting this too high can cause the
|
||||
game to use a lot of memory.
|
||||
</>
|
||||
}
|
||||
marks
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Autosave interval (s)"
|
||||
value={autosaveInterval}
|
||||
callback={handleAutosaveIntervalChange}
|
||||
step={30}
|
||||
min={0}
|
||||
max={600}
|
||||
tooltip={<>The time (in seconds) between each autosave. Set to 0 to disable autosave.</>}
|
||||
marks
|
||||
/>
|
||||
</>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressSavedGameToast}
|
||||
onChange={(newValue) => (Settings.SuppressSavedGameToast = newValue)}
|
||||
text="Suppress Auto-Save Game Toast"
|
||||
tooltip={<>If this is set, there will be no "Game Saved!" toast appearing after an auto-save.</>}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressAutosaveDisabledWarnings}
|
||||
onChange={(newValue) => (Settings.SuppressAutosaveDisabledWarnings = newValue)}
|
||||
text="Suppress Auto-Save Disabled Warning"
|
||||
tooltip={<>If this is set, there will be no warning triggered when auto-save is disabled (at 0).</>}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.SaveGameOnFileSave}
|
||||
onChange={(newValue) => (Settings.SaveGameOnFileSave = newValue)}
|
||||
text="Save game on file save"
|
||||
tooltip={<>Save your game any time a file is saved in the script editor.</>}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.ExcludeRunningScriptsFromSave}
|
||||
onChange={(newValue) => (Settings.ExcludeRunningScriptsFromSave = newValue)}
|
||||
text="Exclude Running Scripts from Save"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, the save file will exclude all running scripts. This is only useful if your save is
|
||||
lagging a lot. You'll have to restart your script every time you launch the game.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</GameOptionsPage>
|
||||
),
|
||||
[GameOptionsTab.INTERFACE]: (
|
||||
<GameOptionsPage title="Interface">
|
||||
<OptionSwitch
|
||||
checked={Settings.DisableASCIIArt}
|
||||
onChange={(newValue) => (Settings.DisableASCIIArt = newValue)}
|
||||
text="Disable ascii art"
|
||||
tooltip={<>If this is set all ASCII art will be disabled.</>}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.DisableTextEffects}
|
||||
onChange={(newValue) => (Settings.DisableTextEffects = newValue)}
|
||||
text="Disable text effects"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, text effects will not be displayed. This can help if text is difficult to read in certain
|
||||
areas.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.DisableOverviewProgressBars}
|
||||
onChange={(newValue) => (Settings.DisableOverviewProgressBars = newValue)}
|
||||
text="Disable Overview Progress Bars"
|
||||
tooltip={<>If this is set, the progress bars in the character overview will be hidden.</>}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.UseIEC60027_2}
|
||||
onChange={(newValue) => (Settings.UseIEC60027_2 = newValue)}
|
||||
text="Use GiB instead of GB"
|
||||
tooltip={
|
||||
<>If this is set all references to memory will use GiB instead of GB, in accordance with IEC 60027-2.</>
|
||||
}
|
||||
/>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Terminal commands and log entries will be timestamped. See https://date-fns.org/docs/Getting-Started/
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<TextField
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<Typography
|
||||
color={formatTime(timestampFormat) === "format error" && timestampFormat !== "" ? "error" : "success"}
|
||||
>
|
||||
Timestamp format:
|
||||
</Typography>
|
||||
),
|
||||
}}
|
||||
value={timestampFormat}
|
||||
onChange={handleTimestampFormatChange}
|
||||
placeholder="yyyy-MM-dd hh:mm:ss"
|
||||
/>
|
||||
</Tooltip>
|
||||
<>
|
||||
<Tooltip title={<Typography>Sets the locale for displaying numbers.</Typography>}>
|
||||
<Typography>Locale</Typography>
|
||||
</Tooltip>
|
||||
<Select value={locale} onChange={handleLocaleChange}>
|
||||
<MenuItem value="en">en</MenuItem>
|
||||
<MenuItem value="bg">bg</MenuItem>
|
||||
<MenuItem value="cs">cs</MenuItem>
|
||||
<MenuItem value="da-dk">da-dk</MenuItem>
|
||||
<MenuItem value="de">de</MenuItem>
|
||||
<MenuItem value="en-au">en-au</MenuItem>
|
||||
<MenuItem value="en-gb">en-gb</MenuItem>
|
||||
<MenuItem value="es">es</MenuItem>
|
||||
<MenuItem value="fr">fr</MenuItem>
|
||||
<MenuItem value="hu">hu</MenuItem>
|
||||
<MenuItem value="it">it</MenuItem>
|
||||
<MenuItem value="lv">lv</MenuItem>
|
||||
<MenuItem value="no">no</MenuItem>
|
||||
<MenuItem value="pl">pl</MenuItem>
|
||||
<MenuItem value="ru">ru</MenuItem>
|
||||
</Select>
|
||||
</>
|
||||
</GameOptionsPage>
|
||||
),
|
||||
[GameOptionsTab.GAMEPLAY]: (
|
||||
<GameOptionsPage title="Gameplay">
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressMessages}
|
||||
onChange={(newValue) => (Settings.SuppressMessages = newValue)}
|
||||
text="Suppress story messages"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, then any messages you receive will not appear as popups on the screen. They will still get
|
||||
sent to your home computer as '.msg' files and can be viewed with the 'cat' Terminal command.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressFactionInvites}
|
||||
onChange={(newValue) => (Settings.SuppressFactionInvites = newValue)}
|
||||
text="Suppress faction invites"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, then any faction invites you receive will not appear as popups on the screen. Your
|
||||
outstanding faction invites can be viewed in the 'Factions' page.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressTravelConfirmation}
|
||||
onChange={(newValue) => (Settings.SuppressTravelConfirmation = newValue)}
|
||||
text="Suppress travel confirmations"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, the confirmation message before traveling will not show up. You will automatically be
|
||||
deducted the travel cost as soon as you click.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressBuyAugmentationConfirmation}
|
||||
onChange={(newValue) => (Settings.SuppressBuyAugmentationConfirmation = newValue)}
|
||||
text="Suppress augmentations confirmation"
|
||||
tooltip={<>If this is set, the confirmation message before buying augmentation will not show up.</>}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressTIXPopup}
|
||||
onChange={(newValue) => (Settings.SuppressTIXPopup = newValue)}
|
||||
text="Suppress TIX messages"
|
||||
tooltip={<>If this is set, the stock market will never create any popup.</>}
|
||||
/>
|
||||
{props.player.bladeburner && (
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressBladeburnerPopup}
|
||||
onChange={(newValue) => (Settings.SuppressBladeburnerPopup = newValue)}
|
||||
text="Suppress bladeburner popup"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, then having your Bladeburner actions interrupted by being busy with something else will
|
||||
not display a popup message.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</GameOptionsPage>
|
||||
),
|
||||
[GameOptionsTab.MISC]: (
|
||||
<GameOptionsPage title="Misc">
|
||||
<OptionSwitch
|
||||
checked={Settings.DisableHotkeys}
|
||||
onChange={(newValue) => (Settings.DisableHotkeys = newValue)}
|
||||
text="Disable hotkeys"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, then most hotkeys (keyboard shortcuts) in the game are disabled. This includes Terminal
|
||||
commands, hotkeys to navigate between different parts of the game, and the "Save and Close (Ctrl + b)"
|
||||
hotkey in the Text Editor.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.EnableBashHotkeys}
|
||||
onChange={(newValue) => (Settings.EnableBashHotkeys = newValue)}
|
||||
text="Enable bash hotkeys"
|
||||
tooltip={
|
||||
<>
|
||||
Improved Bash emulation mode. Setting this to 1 enables several new Terminal shortcuts and features that
|
||||
more closely resemble a real Bash-style shell. Note that when this mode is enabled, the default browser
|
||||
shortcuts are overriden by the new Bash shortcuts.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</GameOptionsPage>
|
||||
),
|
||||
};
|
||||
|
||||
return pages[props.currentTab];
|
||||
};
|
29
src/GameOptions/ui/GameOptionsPage.tsx
Normal file
29
src/GameOptions/ui/GameOptionsPage.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { List, ListItem, Paper, Typography } from "@mui/material";
|
||||
import { uniqueId } from "lodash";
|
||||
import React from "react";
|
||||
|
||||
interface IProps {
|
||||
children: React.ReactElement | (React.ReactElement | null)[];
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const GameOptionsPage = (props: IProps): React.ReactElement => {
|
||||
return (
|
||||
<Paper sx={{ height: "fit-content", p: 1 }}>
|
||||
<Typography variant="h6">{props.title}</Typography>
|
||||
{(props.children as any)?.length > 1 ? (
|
||||
<List disablePadding dense>
|
||||
{(props.children as React.ReactElement[])
|
||||
.filter((c) => c)
|
||||
.map((c, i) => (
|
||||
<ListItem key={uniqueId(String(i))} sx={{ px: 0, display: "block" }}>
|
||||
{c}
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
) : (
|
||||
props.children
|
||||
)}
|
||||
</Paper>
|
||||
);
|
||||
};
|
39
src/GameOptions/ui/GameOptionsRoot.tsx
Normal file
39
src/GameOptions/ui/GameOptionsRoot.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { Box, Container, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { GameOptionsTab } from "../GameOptionsTab";
|
||||
import { CurrentOptionsPage } from "./CurrentOptionsPage";
|
||||
import { GameOptionsSidebar } from "./GameOptionsSidebar";
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
router: IRouter;
|
||||
save: () => void;
|
||||
export: () => void;
|
||||
forceKill: () => void;
|
||||
softReset: () => void;
|
||||
}
|
||||
|
||||
export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
const [currentTab, setCurrentTab] = useState(GameOptionsTab.SYSTEM);
|
||||
|
||||
return (
|
||||
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
|
||||
<Typography variant="h4">Options</Typography>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 3fr", gap: 1 }}>
|
||||
<GameOptionsSidebar
|
||||
tab={currentTab}
|
||||
setTab={(tab: GameOptionsTab) => setCurrentTab(tab)}
|
||||
player={props.player}
|
||||
router={props.router}
|
||||
save={props.save}
|
||||
export={props.export}
|
||||
forceKill={props.forceKill}
|
||||
softReset={props.softReset}
|
||||
/>
|
||||
<CurrentOptionsPage currentTab={currentTab} player={props.player} />
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
336
src/GameOptions/ui/GameOptionsSidebar.tsx
Normal file
336
src/GameOptions/ui/GameOptionsSidebar.tsx
Normal file
@ -0,0 +1,336 @@
|
||||
import {
|
||||
BugReport,
|
||||
Chat,
|
||||
Download,
|
||||
LibraryBooks,
|
||||
Palette,
|
||||
Public,
|
||||
Reddit,
|
||||
Save,
|
||||
SystemUpdateAlt,
|
||||
Upload,
|
||||
Bloodtype,
|
||||
} from "@mui/icons-material";
|
||||
import { Box, Button, List, ListItemButton, Paper, Tooltip, Typography } from "@mui/material";
|
||||
import { default as React, useRef, useState } from "react";
|
||||
import { FileDiagnosticModal } from "../../Diagnostic/FileDiagnosticModal";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { ImportData, saveObject } from "../../SaveObject";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { StyleEditorButton } from "../../Themes/ui/StyleEditorButton";
|
||||
import { ThemeEditorButton } from "../../Themes/ui/ThemeEditorButton";
|
||||
import { ConfirmationModal } from "../../ui/React/ConfirmationModal";
|
||||
import { DeleteGameButton } from "../../ui/React/DeleteGameButton";
|
||||
import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar";
|
||||
import { SoftResetButton } from "../../ui/React/SoftResetButton";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { GameOptionsTab } from "../GameOptionsTab";
|
||||
|
||||
interface IProps {
|
||||
tab: GameOptionsTab;
|
||||
setTab: (tab: GameOptionsTab) => void;
|
||||
player: IPlayer;
|
||||
router: IRouter;
|
||||
save: () => void;
|
||||
export: () => void;
|
||||
forceKill: () => void;
|
||||
softReset: () => void;
|
||||
}
|
||||
|
||||
interface ITabProps {
|
||||
sideBarProps: IProps;
|
||||
tab: GameOptionsTab;
|
||||
tabName: string;
|
||||
}
|
||||
|
||||
const SideBarTab = (props: ITabProps): React.ReactElement => {
|
||||
return (
|
||||
<ListItemButton
|
||||
selected={props.sideBarProps.tab === props.tab}
|
||||
onClick={() => props.sideBarProps.setTab(props.tab)}
|
||||
>
|
||||
<Typography>{props.tabName}</Typography>
|
||||
</ListItemButton>
|
||||
);
|
||||
};
|
||||
|
||||
export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
|
||||
const importInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [diagnosticOpen, setDiagnosticOpen] = useState(false);
|
||||
const [importSaveOpen, setImportSaveOpen] = useState(false);
|
||||
const [importData, setImportData] = useState<ImportData | null>(null);
|
||||
|
||||
function startImport(): void {
|
||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob) return;
|
||||
const ii = importInput.current;
|
||||
if (ii === null) throw new Error("import input should not be null");
|
||||
ii.click();
|
||||
}
|
||||
|
||||
async function onImport(event: React.ChangeEvent<HTMLInputElement>): Promise<void> {
|
||||
try {
|
||||
const base64Save = await saveObject.getImportStringFromFile(event.target.files);
|
||||
const data = await saveObject.getImportDataFromString(base64Save);
|
||||
setImportData(data);
|
||||
setImportSaveOpen(true);
|
||||
} catch (ex: any) {
|
||||
SnackbarEvents.emit(ex.toString(), ToastVariant.ERROR, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmedImportGame(): Promise<void> {
|
||||
if (!importData) return;
|
||||
|
||||
try {
|
||||
await saveObject.importGame(importData.base64);
|
||||
} catch (ex: any) {
|
||||
SnackbarEvents.emit(ex.toString(), ToastVariant.ERROR, 5000);
|
||||
}
|
||||
|
||||
setImportSaveOpen(false);
|
||||
setImportData(null);
|
||||
}
|
||||
|
||||
function compareSaveGame(): void {
|
||||
if (!importData) return;
|
||||
props.router.toImportSave(importData.base64);
|
||||
setImportSaveOpen(false);
|
||||
setImportData(null);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Paper sx={{ height: "fit-content", mb: 1 }}>
|
||||
<List>
|
||||
<SideBarTab sideBarProps={props} tab={GameOptionsTab.SYSTEM} tabName="System" />
|
||||
<SideBarTab sideBarProps={props} tab={GameOptionsTab.GAMEPLAY} tabName="Gameplay" />
|
||||
<SideBarTab sideBarProps={props} tab={GameOptionsTab.INTERFACE} tabName="Interface" />
|
||||
<SideBarTab sideBarProps={props} tab={GameOptionsTab.MISC} tabName="Misc" />
|
||||
</List>
|
||||
</Paper>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
width: "100%",
|
||||
height: "fit-content",
|
||||
gridTemplateAreas: `"save delete"
|
||||
"export import"
|
||||
"kill kill"
|
||||
"reset diagnose"
|
||||
"browse browse"
|
||||
"theme style"
|
||||
"links links"
|
||||
"devs devs"`,
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => props.save()} startIcon={<Save />} sx={{ gridArea: "save" }}>
|
||||
Save Game
|
||||
</Button>
|
||||
<Box sx={{ gridArea: "delete", "& .MuiButton-root": { height: "100%", width: "100%" } }}>
|
||||
<DeleteGameButton />
|
||||
</Box>
|
||||
<Tooltip title={<Typography>Export your game to a text file.</Typography>}>
|
||||
<Button onClick={() => props.export()} startIcon={<Download />} sx={{ gridArea: "export" }}>
|
||||
Export Game
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Import your game from a text file.
|
||||
<br />
|
||||
This will <strong>overwrite</strong> your current game. Back it up first!
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Button onClick={startImport} startIcon={<Upload />} sx={{ gridArea: "import" }}>
|
||||
Import Game
|
||||
<input ref={importInput} id="import-game-file-selector" type="file" hidden onChange={onImport} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<ConfirmationModal
|
||||
open={importSaveOpen}
|
||||
onClose={() => setImportSaveOpen(false)}
|
||||
onConfirm={() => confirmedImportGame()}
|
||||
additionalButton={<Button onClick={compareSaveGame}>Compare Save</Button>}
|
||||
confirmationText={
|
||||
<>
|
||||
Importing a new game will <strong>completely wipe</strong> the current data!
|
||||
<br />
|
||||
<br />
|
||||
Make sure to have a backup of your current save file before importing.
|
||||
<br />
|
||||
The file you are attempting to import seems valid.
|
||||
{(importData?.playerData?.lastSave ?? 0) > 0 && (
|
||||
<>
|
||||
<br />
|
||||
<br />
|
||||
The export date of the save file is{" "}
|
||||
<strong>{new Date(importData?.playerData?.lastSave ?? 0).toLocaleString()}</strong>
|
||||
</>
|
||||
)}
|
||||
{(importData?.playerData?.totalPlaytime ?? 0) > 0 && (
|
||||
<>
|
||||
<br />
|
||||
<br />
|
||||
Total play time of imported game:{" "}
|
||||
{convertTimeMsToTimeElapsedString(importData?.playerData?.totalPlaytime ?? 0)}
|
||||
</>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game.
|
||||
After using this, save the game and then reload the page. This is different then normal kill in that
|
||||
normal kill will tell the script to shut down while force kill just removes the references to it (and it
|
||||
should crash on it's own). This will not remove the files on your computer. Just forcefully kill all
|
||||
running instances of all scripts.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Button onClick={() => props.forceKill()} sx={{ gridArea: "kill" }}>
|
||||
Force kill all active scripts
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Box sx={{ gridArea: "reset", "& .MuiButton-root": { height: "100%", width: "100%" } }}>
|
||||
<SoftResetButton
|
||||
noConfirmation={Settings.SuppressBuyAugmentationConfirmation}
|
||||
onTriggered={props.softReset}
|
||||
/>
|
||||
</Box>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If your save file is extremely big you can use this button to view a map of all the files on every server.
|
||||
Be careful there might be spoilers.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Button onClick={() => setDiagnosticOpen(true)} sx={{ gridArea: "diagnose" }}>
|
||||
Diagnose files
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title="Head to the theme browser to see a collection of prebuilt themes.">
|
||||
<Button startIcon={<Palette />} onClick={() => props.router.toThemeBrowser()} sx={{ gridArea: "browse" }}>
|
||||
Theme Browser
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Box sx={{ gridArea: "theme", "& .MuiButton-root": { height: "100%", width: "100%" } }}>
|
||||
<ThemeEditorButton router={props.router} />
|
||||
</Box>
|
||||
<Box sx={{ gridArea: "style", "& .MuiButton-root": { height: "100%", width: "100%" } }}>
|
||||
<StyleEditorButton />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
gridArea: "links",
|
||||
display: "grid",
|
||||
gridTemplateAreas: `"bug changelog"
|
||||
"docs docs"
|
||||
"discord reddit"
|
||||
"plaza plaza"`,
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
my: 1,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
startIcon={<BugReport />}
|
||||
href="https://github.com/danielyxie/bitburner/issues/new"
|
||||
target="_blank"
|
||||
sx={{ gridArea: "bug" }}
|
||||
>
|
||||
Report Bug
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<SystemUpdateAlt />}
|
||||
href="https://bitburner.readthedocs.io/en/latest/changelog.html"
|
||||
target="_blank"
|
||||
sx={{ gridArea: " changelog" }}
|
||||
>
|
||||
Changelog
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<LibraryBooks />}
|
||||
href="https://bitburner.readthedocs.io/en/latest/index.html"
|
||||
target="_blank"
|
||||
sx={{ gridArea: "docs" }}
|
||||
>
|
||||
Documentation
|
||||
</Button>
|
||||
<Button startIcon={<Chat />} href="https://discord.gg/TFc3hKD" target="_blank" sx={{ gridArea: "discord" }}>
|
||||
Discord
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<Reddit />}
|
||||
href="https://www.reddit.com/r/bitburner"
|
||||
target="_blank"
|
||||
sx={{ gridArea: "reddit" }}
|
||||
>
|
||||
Reddit
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<Public />}
|
||||
href="https://plaza.dsolver.ca/games/bitburner"
|
||||
target="_blank"
|
||||
sx={{ gridArea: "plaza" }}
|
||||
>
|
||||
Incremental game plaza
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{!location.href.startsWith("file://") && (
|
||||
<Box sx={{ gridArea: "devs" }}>
|
||||
<form
|
||||
action="https://www.paypal.com/cgi-bin/webscr"
|
||||
method="post"
|
||||
target="_blank"
|
||||
style={{ display: "block" }}
|
||||
>
|
||||
<Button sx={{ width: "100%", display: "flex", flexDirection: "column" }} type="submit">
|
||||
danielyxie / BigD (Original Dev)
|
||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||
<input
|
||||
type="hidden"
|
||||
name="encrypted"
|
||||
value="-----BEGIN PKCS7-----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-----END PKCS7-----"
|
||||
/>
|
||||
<input
|
||||
type="image"
|
||||
// src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif"
|
||||
src="https://www.paypalobjects.com/digitalassets/c/website/marketing/apac/C2/logos-buttons/optimize/26_Yellow_PayPal_Pill_Button.png"
|
||||
name="submit"
|
||||
alt="PayPal - The safer, easier way to pay online!"
|
||||
/>
|
||||
</Button>
|
||||
</form>
|
||||
<Button
|
||||
href="https://www.google.com/search?q=Where+to+donate+blood+near+me%3F"
|
||||
target="_blank"
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
hydroflame (Current Maintainer)
|
||||
<span style={{ display: "flex", alignItems: "center" }}>
|
||||
<Bloodtype sx={{ mb: 0.5, mr: 1 }} />
|
||||
Donate blood!
|
||||
</span>
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<FileDiagnosticModal open={diagnosticOpen} onClose={() => setDiagnosticOpen(false)} />
|
||||
</Box>
|
||||
);
|
||||
};
|
38
src/GameOptions/ui/OptionsSlider.tsx
Normal file
38
src/GameOptions/ui/OptionsSlider.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { Slider, Tooltip, Typography, Box } from "@mui/material";
|
||||
import React from "react";
|
||||
|
||||
interface IProps {
|
||||
value: any;
|
||||
callback: (event: any, newValue: number | number[]) => void;
|
||||
step: number;
|
||||
min: number;
|
||||
max: number;
|
||||
tooltip: React.ReactElement;
|
||||
label: string;
|
||||
marks?: boolean;
|
||||
}
|
||||
|
||||
export const OptionsSlider = (props: IProps): React.ReactElement => {
|
||||
return (
|
||||
<Box>
|
||||
<Tooltip title={<Typography>{props.tooltip}</Typography>}>
|
||||
<Typography>{props.label}</Typography>
|
||||
</Tooltip>
|
||||
<Slider
|
||||
value={props.value}
|
||||
onChange={props.callback}
|
||||
step={props.step}
|
||||
min={props.min}
|
||||
max={props.max}
|
||||
valueLabelDisplay="auto"
|
||||
sx={{
|
||||
"& .MuiSlider-thumb": {
|
||||
height: "12px",
|
||||
width: "12px",
|
||||
},
|
||||
}}
|
||||
marks={props.marks}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -730,7 +730,22 @@ export function NetscriptCorporation(
|
||||
const divisionName = helper.string("hireEmployee", "divisionName", _divisionName);
|
||||
const cityName = helper.city("hireEmployee", "cityName", _cityName);
|
||||
const office = getOffice(divisionName, cityName);
|
||||
return office.hireRandomEmployee();
|
||||
const employee = office.hireRandomEmployee();
|
||||
if (employee === undefined) return undefined;
|
||||
return {
|
||||
name: employee.name,
|
||||
mor: employee.mor,
|
||||
hap: employee.hap,
|
||||
ene: employee.ene,
|
||||
int: employee.int,
|
||||
cha: employee.cha,
|
||||
exp: employee.exp,
|
||||
cre: employee.cre,
|
||||
eff: employee.eff,
|
||||
sal: employee.sal,
|
||||
loc: employee.loc,
|
||||
pos: employee.pos,
|
||||
};
|
||||
},
|
||||
upgradeOfficeSize: function (_divisionName: unknown, _cityName: unknown, _size: unknown): void {
|
||||
checkAccess("upgradeOfficeSize", 8);
|
||||
|
@ -358,7 +358,6 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
|
||||
},
|
||||
purchase4SMarketData: function (): boolean {
|
||||
updateRam("purchase4SMarketData");
|
||||
checkTixApiAccess("purchase4SMarketData");
|
||||
|
||||
if (player.has4SData) {
|
||||
workerScript.log("stock.purchase4SMarketData", () => "Already purchased 4S Market Data.");
|
||||
|
@ -1870,13 +1870,12 @@ export function getNextCompanyPosition(
|
||||
|
||||
export function quitJob(this: IPlayer, company: string): void {
|
||||
if (this.isWorking == true && this.workType.includes("Working for Company") && this.companyName == company) {
|
||||
this.isWorking = false;
|
||||
this.companyName = "";
|
||||
}
|
||||
if (this.companyName === company) {
|
||||
this.companyName = "";
|
||||
this.finishWork(true);
|
||||
}
|
||||
delete this.jobs[company];
|
||||
if (this.companyName === company) {
|
||||
this.companyName = this.hasJob() ? Object.keys(this.jobs)[0] : "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,6 +23,7 @@ import { LocationName } from "./Locations/data/LocationNames";
|
||||
import { SxProps } from "@mui/system";
|
||||
import { PlayerObject } from "./PersonObjects/Player/PlayerObject";
|
||||
import { pushGameSaved } from "./Electron";
|
||||
import { defaultMonacoTheme } from "./ScriptEditor/ui/themes";
|
||||
|
||||
/* SaveObject.js
|
||||
* Defines the object used to save/load games
|
||||
@ -394,8 +395,8 @@ function evaluateVersionCompatibility(ver: string | number): void {
|
||||
delete anyPlayer.resleeves;
|
||||
}
|
||||
}
|
||||
if (ver < 14) {
|
||||
delete (Settings as any).EditorTheme;
|
||||
if (ver < 15) {
|
||||
(Settings as any).EditorTheme = { ...defaultMonacoTheme };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ import Typography from "@mui/material/Typography";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Popper from "@mui/material/Popper";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { ITerminal } from "../ITerminal";
|
||||
@ -376,46 +377,40 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
title={
|
||||
possibilities.length > 0 ? (
|
||||
<>
|
||||
<Typography classes={{ root: classes.preformatted }} color={"primary"} paragraph={false}>
|
||||
Possible autocomplete candidate:
|
||||
</Typography>
|
||||
<Typography classes={{ root: classes.preformatted }} color={"primary"} paragraph={false}>
|
||||
{possibilities.join(" ")}
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
)
|
||||
}
|
||||
>
|
||||
<TextField
|
||||
fullWidth
|
||||
color={terminal.action === null ? "primary" : "secondary"}
|
||||
autoFocus
|
||||
disabled={terminal.action !== null}
|
||||
autoComplete="off"
|
||||
value={value}
|
||||
classes={{ root: classes.textfield }}
|
||||
onChange={handleValueChange}
|
||||
inputRef={terminalInput}
|
||||
InputProps={{
|
||||
// for players to hook in
|
||||
id: "terminal-input",
|
||||
className: classes.input,
|
||||
startAdornment: (
|
||||
<Typography color={terminal.action === null ? "primary" : "secondary"} flexShrink={0}>
|
||||
[{player.getCurrentServer().hostname} ~{terminal.cwd()}]>
|
||||
</Typography>
|
||||
),
|
||||
spellCheck: false,
|
||||
onKeyDown: onKeyDown,
|
||||
}}
|
||||
></TextField>
|
||||
</Tooltip>
|
||||
<TextField
|
||||
fullWidth
|
||||
color={terminal.action === null ? "primary" : "secondary"}
|
||||
autoFocus
|
||||
disabled={terminal.action !== null}
|
||||
autoComplete="off"
|
||||
value={value}
|
||||
classes={{ root: classes.textfield }}
|
||||
onChange={handleValueChange}
|
||||
inputRef={terminalInput}
|
||||
InputProps={{
|
||||
// for players to hook in
|
||||
id: "terminal-input",
|
||||
className: classes.input,
|
||||
startAdornment: (
|
||||
<Typography color={terminal.action === null ? "primary" : "secondary"} flexShrink={0}>
|
||||
[{player.getCurrentServer().hostname} ~{terminal.cwd()}]>
|
||||
</Typography>
|
||||
),
|
||||
spellCheck: false,
|
||||
onBlur: () => setPossibilities([]),
|
||||
onKeyDown: onKeyDown,
|
||||
}}
|
||||
></TextField>
|
||||
<Popper open={possibilities.length > 0} anchorEl={terminalInput.current} placement={"top-end"}>
|
||||
<Paper sx={{ m: 1, p: 2 }}>
|
||||
<Typography classes={{ root: classes.preformatted }} color={"primary"} paragraph={false}>
|
||||
Possible autocomplete candidates:
|
||||
</Typography>
|
||||
<Typography classes={{ root: classes.preformatted }} color={"primary"} paragraph={false}>
|
||||
{possibilities.join(" ")}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Popper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -82,13 +82,12 @@ function CurrentBitNode(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
if (player.sourceFiles.length > 0) {
|
||||
const index = "BitNode" + player.bitNodeN;
|
||||
const currentSourceFile = player.sourceFiles.find((sourceFile) => sourceFile.n == player.bitNodeN);
|
||||
const lvl = currentSourceFile ? currentSourceFile.lvl : 0;
|
||||
const lvl = player.sourceFileLvl(player.bitNodeN) + 1;
|
||||
return (
|
||||
<Box>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5">
|
||||
BitNode {player.bitNodeN}: {BitNodes[index].name} (Level {lvl + 1})
|
||||
BitNode {player.bitNodeN}: {BitNodes[index].name} (Level {lvl})
|
||||
</Typography>
|
||||
<Typography sx={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>{BitNodes[index].info}</Typography>
|
||||
</Paper>
|
||||
|
@ -44,7 +44,7 @@ import { CorporationRoot } from "../Corporation/ui/CorporationRoot";
|
||||
import { InfiltrationRoot } from "../Infiltration/ui/InfiltrationRoot";
|
||||
import { GraftingRoot } from "../PersonObjects/Grafting/ui/GraftingRoot";
|
||||
import { WorkInProgressRoot } from "./WorkInProgressRoot";
|
||||
import { GameOptionsRoot } from "./React/GameOptionsRoot";
|
||||
import { GameOptionsRoot } from "../GameOptions/ui/GameOptionsRoot";
|
||||
import { SleeveRoot } from "../PersonObjects/Sleeve/ui/SleeveRoot";
|
||||
import { HacknetRoot } from "../Hacknet/ui/HacknetRoot";
|
||||
import { GenericLocation } from "../Locations/ui/GenericLocation";
|
||||
|
@ -1,667 +0,0 @@
|
||||
import React, { useState, useRef } from "react";
|
||||
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Slider from "@mui/material/Slider";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
import Box from "@mui/material/Box";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import Link from "@mui/material/Link";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import UploadIcon from "@mui/icons-material/Upload";
|
||||
import SaveIcon from "@mui/icons-material/Save";
|
||||
import PaletteIcon from "@mui/icons-material/Palette";
|
||||
|
||||
import { FileDiagnosticModal } from "../../Diagnostic/FileDiagnosticModal";
|
||||
import { ConfirmationModal } from "./ConfirmationModal";
|
||||
|
||||
import { SnackbarEvents, ToastVariant } from "./Snackbar";
|
||||
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { DeleteGameButton } from "./DeleteGameButton";
|
||||
import { SoftResetButton } from "./SoftResetButton";
|
||||
import { IRouter } from "../Router";
|
||||
import { ThemeEditorButton } from "../../Themes/ui/ThemeEditorButton";
|
||||
import { StyleEditorButton } from "../../Themes/ui/StyleEditorButton";
|
||||
import { formatTime } from "../../utils/helpers/formatTime";
|
||||
import { OptionSwitch } from "./OptionSwitch";
|
||||
import { ImportData, saveObject } from "../../SaveObject";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
width: 50,
|
||||
padding: theme.spacing(2),
|
||||
userSelect: "none",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
router: IRouter;
|
||||
save: () => void;
|
||||
export: () => void;
|
||||
forceKill: () => void;
|
||||
softReset: () => void;
|
||||
}
|
||||
|
||||
export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
const importInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [execTime, setExecTime] = useState(Settings.CodeInstructionRunTime);
|
||||
const [recentScriptsSize, setRecentScriptsSize] = useState(Settings.MaxRecentScriptsCapacity);
|
||||
const [logSize, setLogSize] = useState(Settings.MaxLogCapacity);
|
||||
const [portSize, setPortSize] = useState(Settings.MaxPortCapacity);
|
||||
const [terminalSize, setTerminalSize] = useState(Settings.MaxTerminalCapacity);
|
||||
const [autosaveInterval, setAutosaveInterval] = useState(Settings.AutosaveInterval);
|
||||
const [timestampFormat, setTimestampFormat] = useState(Settings.TimestampsFormat);
|
||||
const [locale, setLocale] = useState(Settings.Locale);
|
||||
const [diagnosticOpen, setDiagnosticOpen] = useState(false);
|
||||
const [importSaveOpen, setImportSaveOpen] = useState(false);
|
||||
const [importData, setImportData] = useState<ImportData | null>(null);
|
||||
|
||||
function handleExecTimeChange(event: any, newValue: number | number[]): void {
|
||||
setExecTime(newValue as number);
|
||||
Settings.CodeInstructionRunTime = newValue as number;
|
||||
}
|
||||
|
||||
function handleRecentScriptsSizeChange(event: any, newValue: number | number[]): void {
|
||||
setRecentScriptsSize(newValue as number);
|
||||
Settings.MaxRecentScriptsCapacity = newValue as number;
|
||||
}
|
||||
|
||||
function handleLogSizeChange(event: any, newValue: number | number[]): void {
|
||||
setLogSize(newValue as number);
|
||||
Settings.MaxLogCapacity = newValue as number;
|
||||
}
|
||||
|
||||
function handlePortSizeChange(event: any, newValue: number | number[]): void {
|
||||
setPortSize(newValue as number);
|
||||
Settings.MaxPortCapacity = newValue as number;
|
||||
}
|
||||
|
||||
function handleTerminalSizeChange(event: any, newValue: number | number[]): void {
|
||||
setTerminalSize(newValue as number);
|
||||
Settings.MaxTerminalCapacity = newValue as number;
|
||||
}
|
||||
|
||||
function handleAutosaveIntervalChange(event: any, newValue: number | number[]): void {
|
||||
setAutosaveInterval(newValue as number);
|
||||
Settings.AutosaveInterval = newValue as number;
|
||||
}
|
||||
|
||||
function handleLocaleChange(event: SelectChangeEvent<string>): void {
|
||||
setLocale(event.target.value as string);
|
||||
Settings.Locale = event.target.value as string;
|
||||
}
|
||||
|
||||
function handleTimestampFormatChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setTimestampFormat(event.target.value);
|
||||
Settings.TimestampsFormat = event.target.value;
|
||||
}
|
||||
|
||||
function startImport(): void {
|
||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob) return;
|
||||
const ii = importInput.current;
|
||||
if (ii === null) throw new Error("import input should not be null");
|
||||
ii.click();
|
||||
}
|
||||
|
||||
async function onImport(event: React.ChangeEvent<HTMLInputElement>): Promise<void> {
|
||||
try {
|
||||
const base64Save = await saveObject.getImportStringFromFile(event.target.files);
|
||||
const data = await saveObject.getImportDataFromString(base64Save);
|
||||
setImportData(data);
|
||||
setImportSaveOpen(true);
|
||||
} catch (ex: any) {
|
||||
SnackbarEvents.emit(ex.toString(), ToastVariant.ERROR, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmedImportGame(): Promise<void> {
|
||||
if (!importData) return;
|
||||
|
||||
try {
|
||||
await saveObject.importGame(importData.base64);
|
||||
} catch (ex: any) {
|
||||
SnackbarEvents.emit(ex.toString(), ToastVariant.ERROR, 5000);
|
||||
}
|
||||
|
||||
setImportSaveOpen(false);
|
||||
setImportData(null);
|
||||
}
|
||||
|
||||
function compareSaveGame(): void {
|
||||
if (!importData) return;
|
||||
props.router.toImportSave(importData.base64);
|
||||
setImportSaveOpen(false);
|
||||
setImportData(null);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.root} style={{ width: "90%" }}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Options
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<List>
|
||||
<ListItem>
|
||||
<Box display="grid" sx={{ width: "fit-content", gridTemplateColumns: "1fr 3.5fr", gap: 1 }}>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The minimum number of milliseconds it takes to execute an operation in Netscript. Setting this too
|
||||
low can result in poor performance if you have many scripts running.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>.script exec time (ms)</Typography>
|
||||
</Tooltip>
|
||||
<Slider
|
||||
value={execTime}
|
||||
onChange={handleExecTimeChange}
|
||||
step={1}
|
||||
min={5}
|
||||
max={100}
|
||||
valueLabelDisplay="auto"
|
||||
/>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The maximum number of recently killed script entries being tracked. Setting this too high can
|
||||
cause the game to use a lot of memory.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Recently killed scripts size</Typography>
|
||||
</Tooltip>
|
||||
<Slider
|
||||
value={recentScriptsSize}
|
||||
onChange={handleRecentScriptsSizeChange}
|
||||
step={25}
|
||||
min={0}
|
||||
max={500}
|
||||
valueLabelDisplay="auto"
|
||||
/>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The maximum number of lines a script's logs can hold. Setting this too high can cause the game to
|
||||
use a lot of memory if you have many scripts running.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Netscript log size</Typography>
|
||||
</Tooltip>
|
||||
<Slider
|
||||
value={logSize}
|
||||
onChange={handleLogSizeChange}
|
||||
step={20}
|
||||
min={20}
|
||||
max={500}
|
||||
valueLabelDisplay="auto"
|
||||
/>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The maximum number of entries that can be written to a port using Netscript's write() function.
|
||||
Setting this too high can cause the game to use a lot of memory.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Netscript port size</Typography>
|
||||
</Tooltip>
|
||||
<Slider
|
||||
value={portSize}
|
||||
onChange={handlePortSizeChange}
|
||||
step={1}
|
||||
min={20}
|
||||
max={100}
|
||||
valueLabelDisplay="auto"
|
||||
/>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The maximum number of entries that can be written to the terminal. Setting this too high can cause
|
||||
the game to use a lot of memory.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Terminal capacity</Typography>
|
||||
</Tooltip>
|
||||
<Slider
|
||||
value={terminalSize}
|
||||
onChange={handleTerminalSizeChange}
|
||||
step={50}
|
||||
min={50}
|
||||
max={500}
|
||||
valueLabelDisplay="auto"
|
||||
marks
|
||||
/>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>The time (in seconds) between each autosave. Set to 0 to disable autosave.</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Autosave interval (s)</Typography>
|
||||
</Tooltip>
|
||||
<Slider
|
||||
value={autosaveInterval}
|
||||
onChange={handleAutosaveIntervalChange}
|
||||
step={30}
|
||||
min={0}
|
||||
max={600}
|
||||
valueLabelDisplay="auto"
|
||||
marks
|
||||
/>
|
||||
</Box>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressMessages}
|
||||
onChange={(newValue) => (Settings.SuppressMessages = newValue)}
|
||||
text="Suppress story messages"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, then any messages you receive will not appear as popups on the screen. They will
|
||||
still get sent to your home computer as '.msg' files and can be viewed with the 'cat' Terminal
|
||||
command.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressFactionInvites}
|
||||
onChange={(newValue) => (Settings.SuppressFactionInvites = newValue)}
|
||||
text="Suppress faction invites"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, then any faction invites you receive will not appear as popups on the screen. Your
|
||||
outstanding faction invites can be viewed in the 'Factions' page.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressTravelConfirmation}
|
||||
onChange={(newValue) => (Settings.SuppressTravelConfirmation = newValue)}
|
||||
text="Suppress travel confirmations"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, the confirmation message before traveling will not show up. You will automatically
|
||||
be deducted the travel cost as soon as you click.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressBuyAugmentationConfirmation}
|
||||
onChange={(newValue) => (Settings.SuppressBuyAugmentationConfirmation = newValue)}
|
||||
text="Suppress augmentations confirmation"
|
||||
tooltip={<>If this is set, the confirmation message before buying augmentation will not show up.</>}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressTIXPopup}
|
||||
onChange={(newValue) => (Settings.SuppressTIXPopup = newValue)}
|
||||
text="Suppress TIX messages"
|
||||
tooltip={<>If this is set, the stock market will never create any popup.</>}
|
||||
/>
|
||||
</ListItem>
|
||||
{!!props.player.bladeburner && (
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressBladeburnerPopup}
|
||||
onChange={(newValue) => (Settings.SuppressBladeburnerPopup = newValue)}
|
||||
text="Suppress bladeburner popup"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, then having your Bladeburner actions interrupted by being busy with something else
|
||||
will not display a popup message.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressSavedGameToast}
|
||||
onChange={(newValue) => (Settings.SuppressSavedGameToast = newValue)}
|
||||
text="Suppress Auto-Save Game Toast"
|
||||
tooltip={<>If this is set, there will be no "Game Saved!" toast appearing after an auto-save.</>}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.SuppressAutosaveDisabledWarnings}
|
||||
onChange={(newValue) => (Settings.SuppressAutosaveDisabledWarnings = newValue)}
|
||||
text="Suppress Auto-Save Disabled Warning"
|
||||
tooltip={<>If this is set, there will be no warning triggered when auto-save is disabled (at 0).</>}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.DisableHotkeys}
|
||||
onChange={(newValue) => (Settings.DisableHotkeys = newValue)}
|
||||
text="Disable hotkeys"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, then most hotkeys (keyboard shortcuts) in the game are disabled. This includes
|
||||
Terminal commands, hotkeys to navigate between different parts of the game, and the "Save and Close
|
||||
(Ctrl + b)" hotkey in the Text Editor.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.DisableASCIIArt}
|
||||
onChange={(newValue) => (Settings.DisableASCIIArt = newValue)}
|
||||
text="Disable ascii art"
|
||||
tooltip={<>If this is set all ASCII art will be disabled.</>}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.DisableTextEffects}
|
||||
onChange={(newValue) => (Settings.DisableTextEffects = newValue)}
|
||||
text="Disable text effects"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, text effects will not be displayed. This can help if text is difficult to read in
|
||||
certain areas.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.DisableOverviewProgressBars}
|
||||
onChange={(newValue) => (Settings.DisableOverviewProgressBars = newValue)}
|
||||
text="Disable Overview Progress Bars"
|
||||
tooltip={<>If this is set, the progress bars in the character overview will be hidden.</>}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.EnableBashHotkeys}
|
||||
onChange={(newValue) => (Settings.EnableBashHotkeys = newValue)}
|
||||
text="Enable bash hotkeys"
|
||||
tooltip={
|
||||
<>
|
||||
Improved Bash emulation mode. Setting this to 1 enables several new Terminal shortcuts and features
|
||||
that more closely resemble a real Bash-style shell. Note that when this mode is enabled, the default
|
||||
browser shortcuts are overriden by the new Bash shortcuts.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.UseIEC60027_2}
|
||||
onChange={(newValue) => (Settings.UseIEC60027_2 = newValue)}
|
||||
text="Use GiB instead of GB"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set all references to memory will use GiB instead of GB, in accordance with IEC 60027-2.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.ExcludeRunningScriptsFromSave}
|
||||
onChange={(newValue) => (Settings.ExcludeRunningScriptsFromSave = newValue)}
|
||||
text="Exclude Running Scripts from Save"
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, the save file will exclude all running scripts. This is only useful if your save is
|
||||
lagging a lot. You'll have to restart your script every time you launch the game.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Terminal commands and log entries will be timestamped. See
|
||||
https://date-fns.org/docs/Getting-Started/
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<TextField
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<Typography
|
||||
color={
|
||||
formatTime(timestampFormat) === "format error" && timestampFormat !== ""
|
||||
? "error"
|
||||
: "success"
|
||||
}
|
||||
>
|
||||
Timestamp format:
|
||||
</Typography>
|
||||
),
|
||||
}}
|
||||
value={timestampFormat}
|
||||
onChange={handleTimestampFormatChange}
|
||||
placeholder="yyyy-MM-dd hh:mm:ss"
|
||||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<OptionSwitch
|
||||
checked={Settings.SaveGameOnFileSave}
|
||||
onChange={(newValue) => (Settings.SaveGameOnFileSave = newValue)}
|
||||
text="Save game on file save"
|
||||
tooltip={<>Save your game any time a file is saved in the script editor.</>}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<Tooltip title={<Typography>Sets the locale for displaying numbers.</Typography>}>
|
||||
<Typography>Locale </Typography>
|
||||
</Tooltip>
|
||||
<Select value={locale} onChange={handleLocaleChange}>
|
||||
<MenuItem value="en">en</MenuItem>
|
||||
<MenuItem value="bg">bg</MenuItem>
|
||||
<MenuItem value="cs">cs</MenuItem>
|
||||
<MenuItem value="da-dk">da-dk</MenuItem>
|
||||
<MenuItem value="de">de</MenuItem>
|
||||
<MenuItem value="en-au">en-au</MenuItem>
|
||||
<MenuItem value="en-gb">en-gb</MenuItem>
|
||||
<MenuItem value="es">es</MenuItem>
|
||||
<MenuItem value="fr">fr</MenuItem>
|
||||
<MenuItem value="hu">hu</MenuItem>
|
||||
<MenuItem value="it">it</MenuItem>
|
||||
<MenuItem value="lv">lv</MenuItem>
|
||||
<MenuItem value="no">no</MenuItem>
|
||||
<MenuItem value="pl">pl</MenuItem>
|
||||
<MenuItem value="ru">ru</MenuItem>
|
||||
</Select>
|
||||
</ListItem>
|
||||
</List>
|
||||
{!location.href.startsWith("file://") && (
|
||||
<>
|
||||
<ListItem>
|
||||
<Typography>danielyxie / BigD (Original developer): </Typography>
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
|
||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||
<input
|
||||
type="hidden"
|
||||
name="encrypted"
|
||||
value="-----BEGIN PKCS7-----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-----END PKCS7-----"
|
||||
/>
|
||||
<input
|
||||
type="image"
|
||||
src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif"
|
||||
name="submit"
|
||||
alt="PayPal - The safer, easier way to pay online!"
|
||||
/>
|
||||
</form>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<Typography>
|
||||
hydroflame (Current maintainer):{" "}
|
||||
<Link href="https://www.google.com/search?q=Where+to+donate+blood+near+me%3F" target="_blank">
|
||||
Donate blood!
|
||||
</Link>{" "}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
<Box sx={{ display: "grid", width: "fit-content", height: "fit-content" }}>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
|
||||
<Button onClick={() => props.save()} startIcon={<SaveIcon />}>
|
||||
Save Game
|
||||
</Button>
|
||||
<DeleteGameButton />
|
||||
</Box>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
|
||||
<Tooltip title={<Typography>Export your game to a text file.</Typography>}>
|
||||
<Button onClick={() => props.export()} startIcon={<DownloadIcon />}>
|
||||
Export Game
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Import your game from a text file.
|
||||
<br />
|
||||
This will <strong>overwrite</strong> your current game. Back it up first!
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Button onClick={startImport} startIcon={<UploadIcon />}>
|
||||
Import Game
|
||||
<input ref={importInput} id="import-game-file-selector" type="file" hidden onChange={onImport} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<ConfirmationModal
|
||||
open={importSaveOpen}
|
||||
onClose={() => setImportSaveOpen(false)}
|
||||
onConfirm={() => confirmedImportGame()}
|
||||
additionalButton={<Button onClick={compareSaveGame}>Compare Save</Button>}
|
||||
confirmationText={
|
||||
<>
|
||||
Importing a new game will <strong>completely wipe</strong> the current data!
|
||||
<br />
|
||||
<br />
|
||||
Make sure to have a backup of your current save file before importing.
|
||||
<br />
|
||||
The file you are attempting to import seems valid.
|
||||
{(importData?.playerData?.lastSave ?? 0) > 0 && (
|
||||
<>
|
||||
<br />
|
||||
<br />
|
||||
The export date of the save file is{" "}
|
||||
<strong>{new Date(importData?.playerData?.lastSave ?? 0).toLocaleString()}</strong>
|
||||
</>
|
||||
)}
|
||||
{(importData?.playerData?.totalPlaytime ?? 0) > 0 && (
|
||||
<>
|
||||
<br />
|
||||
<br />
|
||||
Total play time of imported game:{" "}
|
||||
{convertTimeMsToTimeElapsedString(importData?.playerData?.totalPlaytime ?? 0)}
|
||||
</>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<Box sx={{ display: "grid" }}>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the
|
||||
game. After using this, save the game and then reload the page. This is different then normal kill in
|
||||
that normal kill will tell the script to shut down while force kill just removes the references to it
|
||||
(and it should crash on it's own). This will not remove the files on your computer. Just forcefully
|
||||
kill all running instance of all scripts.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Button onClick={() => props.forceKill()}>Force kill all active scripts</Button>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
|
||||
<SoftResetButton
|
||||
noConfirmation={Settings.SuppressBuyAugmentationConfirmation}
|
||||
onTriggered={props.softReset}
|
||||
/>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If your save file is extremely big you can use this button to view a map of all the files on every
|
||||
server. Be careful there might be spoilers.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Button onClick={() => setDiagnosticOpen(true)}>Diagnose files</Button>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr" }}>
|
||||
<Tooltip title="Head to the theme browser to see a collection of prebuilt themes.">
|
||||
<Button startIcon={<PaletteIcon />} onClick={() => props.router.toThemeBrowser()}>
|
||||
Theme Browser
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<ThemeEditorButton router={props.router} />
|
||||
<StyleEditorButton />
|
||||
</Box>
|
||||
<Box>
|
||||
<Link href="https://github.com/danielyxie/bitburner/issues/new" target="_blank">
|
||||
<Typography>Report bug</Typography>
|
||||
</Link>
|
||||
<Link href="https://bitburner.readthedocs.io/en/latest/changelog.html" target="_blank">
|
||||
<Typography>Changelog</Typography>
|
||||
</Link>
|
||||
<Link href="https://bitburner.readthedocs.io/en/latest/index.html" target="_blank">
|
||||
<Typography>Documentation</Typography>
|
||||
</Link>
|
||||
<Link href="https://discord.gg/TFc3hKD" target="_blank">
|
||||
<Typography>Discord</Typography>
|
||||
</Link>
|
||||
<Link href="https://www.reddit.com/r/bitburner" target="_blank">
|
||||
<Typography>Reddit</Typography>
|
||||
</Link>
|
||||
<Link href="https://plaza.dsolver.ca/games/bitburner" target="_blank">
|
||||
<Typography>Incremental game plaza</Typography>
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
<FileDiagnosticModal open={diagnosticOpen} onClose={() => setDiagnosticOpen(false)} />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -265,10 +265,22 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
</Typography>
|
||||
|
||||
<Box position="absolute" right={0}>
|
||||
{!workerScripts.has(script.pid) && <Button onClick={run}>Run</Button>}
|
||||
{workerScripts.has(script.pid) && <Button onClick={kill}>Kill</Button>}
|
||||
<Button onClick={minimize}>{minimized ? "\u{1F5D6}" : "\u{1F5D5}"}</Button>
|
||||
<Button onClick={props.onClose}>Close</Button>
|
||||
{!workerScripts.has(script.pid) && (
|
||||
<Button onClick={run} onTouchEnd={run}>
|
||||
Run
|
||||
</Button>
|
||||
)}
|
||||
{workerScripts.has(script.pid) && (
|
||||
<Button onClick={kill} onTouchEnd={kill}>
|
||||
Kill
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={minimize} onTouchEnd={minimize}>
|
||||
{minimized ? "\u{1F5D6}" : "\u{1F5D5}"}
|
||||
</Button>
|
||||
<Button onClick={props.onClose} onTouchEnd={props.onClose}>
|
||||
Close
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
@ -130,7 +130,14 @@ export function Overview({ children, mode }: IProps): React.ReactElement {
|
||||
size="small"
|
||||
className={classes.visibilityToggle}
|
||||
>
|
||||
{<CurrentIcon className={classes.icon} color="secondary" onClick={() => setOpen((old) => !old)} />}
|
||||
{
|
||||
<CurrentIcon
|
||||
className={classes.icon}
|
||||
color="secondary"
|
||||
onClick={() => setOpen((old) => !old)}
|
||||
onTouchEnd={() => setOpen((old) => !old)}
|
||||
/>
|
||||
}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
Loading…
Reference in New Issue
Block a user