Merge pull request #3893 from danielyxie/dev

2 recovery fix
This commit is contained in:
hydroflame 2022-07-06 12:43:23 -04:00 committed by GitHub
commit b55318706a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 137 additions and 128 deletions

4
dist/main.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

64
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1686,9 +1686,6 @@ export class Bladeburner implements IBladeburner {
// Operation Daedalus // Operation Daedalus
if (action == null) { if (action == null) {
throw new Error("Failed to get BlackOperation Object for: " + this.action.name); throw new Error("Failed to get BlackOperation Object for: " + this.action.name);
} else if (action.name === BlackOperationNames.OperationDaedalus && this.blackops[action.name]) {
this.resetAction();
router.toBitVerse(false, false);
} else if (this.action.type != ActionTypes["BlackOperation"] && this.action.type != ActionTypes["BlackOp"]) { } else if (this.action.type != ActionTypes["BlackOperation"] && this.action.type != ActionTypes["BlackOp"]) {
this.startAction(player, this.action); // Repeat action this.startAction(player, this.action); // Repeat action
} }
@ -1991,11 +1988,6 @@ export class Bladeburner implements IBladeburner {
// Edge race condition when the engine checks the processing counters and attempts to route before the router is initialized. // Edge race condition when the engine checks the processing counters and attempts to route before the router is initialized.
if (!router.isInitialized) return; if (!router.isInitialized) return;
// Edge case condition...if Operation Daedalus is complete trigger the BitNode
if (router.page() !== Page.BitVerse && this.blackops.hasOwnProperty(BlackOperationNames.OperationDaedalus)) {
return router.toBitVerse(false, false);
}
// If the Player starts doing some other actions, set action to idle and alert // If the Player starts doing some other actions, set action to idle and alert
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && player.isWorking) { if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && player.isWorking) {
if (this.action.type !== ActionTypes["Idle"]) { if (this.action.type !== ActionTypes["Idle"]) {

@ -4,6 +4,10 @@ import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
import { use } from "../../ui/Context";
import { BlackOperationNames } from "../data/BlackOperationNames";
import { Button } from "@mui/material";
import { CorruptableText } from "../../ui/React/CorruptableText";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
@ -11,6 +15,7 @@ interface IProps {
} }
export function BlackOpPage(props: IProps): React.ReactElement { export function BlackOpPage(props: IProps): React.ReactElement {
const router = use.Router();
return ( return (
<> <>
<Typography> <Typography>
@ -27,7 +32,13 @@ export function BlackOpPage(props: IProps): React.ReactElement {
Like normal operations, you may use a team for Black Ops. Failing a black op will incur heavy HP and rank Like normal operations, you may use a team for Black Ops. Failing a black op will incur heavy HP and rank
losses. losses.
</Typography> </Typography>
<BlackOpList bladeburner={props.bladeburner} player={props.player} /> {props.bladeburner.blackops[BlackOperationNames.OperationDaedalus] ? (
<Button sx={{ my: 1, p: 1 }} onClick={() => router.toBitVerse(false, false)}>
<CorruptableText content="Destroy w0rld_d34mon"></CorruptableText>
</Button>
) : (
<BlackOpList bladeburner={props.bladeburner} player={props.player} />
)}
</> </>
); );
} }

@ -229,7 +229,7 @@ export const CONSTANTS: {
InfiniteLoopLimit: 2000, InfiniteLoopLimit: 2000,
Donations: 7, Donations: 20,
LatestUpdate: ` LatestUpdate: `
## [draft] v1.7.0 - 2022-04-13 to 2022-05-20 ## [draft] v1.7.0 - 2022-04-13 to 2022-05-20

@ -66,7 +66,7 @@ export function ActiveFragmentSummary(props: IProps): React.ReactElement {
</TableRow> </TableRow>
{summary.map((entry) => { {summary.map((entry) => {
return ( return (
<TableRow> <TableRow key={entry.type}>
<TableCell sx={{ borderBottom: "none", p: 0, m: 0 }}> <TableCell sx={{ borderBottom: "none", p: 0, m: 0 }}>
<Typography> <Typography>
{entry.coordinate.map((coord) => { {entry.coordinate.map((coord) => {

@ -3,7 +3,7 @@ import * as React from "react";
import { ActiveFragment } from "../ActiveFragment"; import { ActiveFragment } from "../ActiveFragment";
import { DummyGift } from "../DummyGift"; import { DummyGift } from "../DummyGift";
import { Grid } from "./Grid"; import { Grid } from "./Grid";
import { calculateGrid, zeros } from "../Helper"; import { zeros } from "../Helper";
interface IProps { interface IProps {
width: number; width: number;
@ -13,7 +13,6 @@ interface IProps {
export function DummyGrid(props: IProps): React.ReactElement { export function DummyGrid(props: IProps): React.ReactElement {
const gift = new DummyGift(props.width, props.height, props.fragments); const gift = new DummyGift(props.width, props.height, props.fragments);
const activeGrid = calculateGrid(gift);
const ghostGrid = zeros([props.width, props.height]); const ghostGrid = zeros([props.width, props.height]);
return ( return (
<Box> <Box>
@ -21,7 +20,6 @@ export function DummyGrid(props: IProps): React.ReactElement {
<Grid <Grid
width={props.width} width={props.width}
height={props.height} height={props.height}
activeGrid={activeGrid}
ghostGrid={ghostGrid} ghostGrid={ghostGrid}
gift={gift} gift={gift}
enter={() => undefined} enter={() => undefined}

@ -1,13 +1,13 @@
import { TableBody, TableRow } from "@mui/material"; import { TableBody, TableRow } from "@mui/material";
import * as React from "react"; import * as React from "react";
import { ActiveFragment } from "../ActiveFragment"; import { ActiveFragment } from "../ActiveFragment";
import { calculateGrid } from "../Helper";
import { IStaneksGift } from "../IStaneksGift"; import { IStaneksGift } from "../IStaneksGift";
import { Cell } from "./Cell"; import { Cell } from "./Cell";
interface IProps { interface IProps {
width: number; width: number;
height: number; height: number;
activeGrid: number[][];
ghostGrid: number[][]; ghostGrid: number[][];
gift: IStaneksGift; gift: IStaneksGift;
enter(i: number, j: number): void; enter(i: number, j: number): void;
@ -32,11 +32,13 @@ function randomColor(fragment: ActiveFragment): string {
} }
export function Grid(props: IProps): React.ReactElement { export function Grid(props: IProps): React.ReactElement {
const activeGrid = calculateGrid(props.gift);
function color(worldX: number, worldY: number): string { function color(worldX: number, worldY: number): string {
if (props.ghostGrid[worldX][worldY] && props.activeGrid[worldX][worldY]) return "red"; if (props.ghostGrid[worldX][worldY] && activeGrid[worldX][worldY]) return "red";
if (props.ghostGrid[worldX][worldY]) return "white"; if (props.ghostGrid[worldX][worldY]) return "white";
if (props.activeGrid[worldX][worldY]) { if (activeGrid[worldX][worldY]) {
const fragment = props.gift.fragmentAt(worldX, worldY); const fragment = props.gift.fragmentAt(worldX, worldY);
if (!fragment) throw new Error("ActiveFragment should not be null"); if (!fragment) throw new Error("ActiveFragment should not be null");
return randomColor(fragment); return randomColor(fragment);

@ -8,7 +8,7 @@ import Box from "@mui/material/Box";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Table } from "../../ui/React/Table"; import { Table } from "../../ui/React/Table";
import { Grid } from "./Grid"; import { Grid } from "./Grid";
import { zeros, calculateGrid } from "../Helper"; import { zeros } from "../Helper";
import { ActiveFragmentSummary } from "./ActiveFragmentSummary"; import { ActiveFragmentSummary } from "./ActiveFragmentSummary";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
@ -18,7 +18,6 @@ interface IProps {
} }
export function MainBoard(props: IProps): React.ReactElement { export function MainBoard(props: IProps): React.ReactElement {
const [grid, setGrid] = React.useState(calculateGrid(props.gift));
const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()])); const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()]));
const [pos, setPos] = React.useState([0, 0]); const [pos, setPos] = React.useState([0, 0]);
const [rotation, setRotation] = React.useState(0); const [rotation, setRotation] = React.useState(0);
@ -54,12 +53,10 @@ export function MainBoard(props: IProps): React.ReactElement {
if (!props.gift.canPlace(worldX, worldY, rotation, selectedFragment)) return; if (!props.gift.canPlace(worldX, worldY, rotation, selectedFragment)) return;
props.gift.place(worldX, worldY, rotation, selectedFragment); props.gift.place(worldX, worldY, rotation, selectedFragment);
} }
setGrid(calculateGrid(props.gift));
} }
function clear(): void { function clear(): void {
props.gift.clear(); props.gift.clear();
setGrid(zeros([props.gift.width(), props.gift.height()]));
} }
function updateSelectedFragment(fragment: Fragment): void { function updateSelectedFragment(fragment: Fragment): void {
@ -92,7 +89,6 @@ export function MainBoard(props: IProps): React.ReactElement {
<Grid <Grid
width={props.gift.width()} width={props.gift.width()}
height={props.gift.height()} height={props.gift.height()}
activeGrid={grid}
ghostGrid={ghostGrid} ghostGrid={ghostGrid}
gift={props.gift} gift={props.gift}
enter={(i, j) => moveGhost(i, j, rotation)} enter={(i, j) => moveGhost(i, j, rotation)}

@ -20,7 +20,7 @@ import { MathJaxWrapper } from "../../MathJaxWrapper";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import { NumberInput } from "../../ui/React/NumberInput";
type IProps = { type IProps = {
faction: Faction; faction: Faction;
@ -31,26 +31,20 @@ type IProps = {
}; };
export function DonateOption(props: IProps): React.ReactElement { export function DonateOption(props: IProps): React.ReactElement {
const [donateAmt, setDonateAmt] = useState<number | null>(null); const [donateAmt, setDonateAmt] = useState<number>(NaN);
const digits = (CONSTANTS.DonateMoneyToRepDivisor + "").length - 1; const digits = (CONSTANTS.DonateMoneyToRepDivisor + "").length - 1;
function canDonate(): boolean { function canDonate(): boolean {
if (donateAmt === null) return false; if (isNaN(donateAmt)) return false;
if (isNaN(donateAmt) || donateAmt <= 0) return false; if (isNaN(donateAmt) || donateAmt <= 0) return false;
if (props.p.money < donateAmt) return false; if (props.p.money < donateAmt) return false;
return true; return true;
} }
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
const amt = numeralWrapper.parseMoney(event.target.value);
if (event.target.value === "" || isNaN(amt)) setDonateAmt(null);
else setDonateAmt(amt);
}
function donate(): void { function donate(): void {
const fac = props.faction; const fac = props.faction;
const amt = donateAmt; const amt = donateAmt;
if (amt === null) return; if (isNaN(amt)) return;
if (!canDonate()) return; if (!canDonate()) return;
props.p.loseMoney(amt, "other"); props.p.loseMoney(amt, "other");
const repGain = repFromDonation(amt, props.p); const repGain = repFromDonation(amt, props.p);
@ -85,8 +79,8 @@ export function DonateOption(props: IProps): React.ReactElement {
</Typography> </Typography>
) : ( ) : (
<> <>
<TextField <NumberInput
onChange={onChange} onChange={setDonateAmt}
placeholder={"Donation amount"} placeholder={"Donation amount"}
disabled={props.disabled} disabled={props.disabled}
InputProps={{ InputProps={{

@ -277,14 +277,6 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
> >
Reddit Reddit
</Button> </Button>
<Button
startIcon={<Public />}
href="https://plaza.dsolver.ca/games/bitburner"
target="_blank"
sx={{ gridArea: "plaza" }}
>
Incremental game plaza
</Button>
</Box> </Box>
{!location.href.startsWith("file://") && ( {!location.href.startsWith("file://") && (

@ -95,7 +95,7 @@ function wrapFunction(
boolean: helpers.boolean, boolean: helpers.boolean,
getServer: (hostname: string) => helpers.getServer(hostname, ctx), getServer: (hostname: string) => helpers.getServer(hostname, ctx),
checkSingularityAccess: () => helpers.checkSingularityAccess(functionName), checkSingularityAccess: () => helpers.checkSingularityAccess(functionName),
hack: helpers.hack, hack: (hostname: any, manual: any, extra?: any) => helpers.hack(ctx, hostname, manual, extra),
getValidPort: (port: any) => helpers.getValidPort(functionPath, port), getValidPort: (port: any) => helpers.getValidPort(functionPath, port),
}, },
}; };

@ -539,8 +539,8 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
stock: NetscriptStockMarket(Player, workerScript), stock: NetscriptStockMarket(Player, workerScript),
grafting: NetscriptGrafting(Player), grafting: NetscriptGrafting(Player),
hacknet: NetscriptHacknet(Player, workerScript), hacknet: NetscriptHacknet(Player, workerScript),
sprintf: sprintf, sprintf: () => sprintf,
vsprintf: vsprintf, vsprintf: () => vsprintf,
scan: scan:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
(_hostname: unknown = workerScript.hostname): string[] => { (_hostname: unknown = workerScript.hostname): string[] => {

@ -725,7 +725,7 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript
const employeeName = ctx.helper.string("employeeName", _employeeName); const employeeName = ctx.helper.string("employeeName", _employeeName);
const job = ctx.helper.string("job", _job); const job = ctx.helper.string("job", _job);
const employee = getEmployee(divisionName, cityName, employeeName); const employee = getEmployee(divisionName, cityName, employeeName);
return netscriptDelay(1000, workerScript).then(function () { return netscriptDelay(["Training", "Unassigned"].includes(employee.pos) ? 0 : 1000, workerScript).then(function () {
return Promise.resolve(AssignJob(employee, job)); return Promise.resolve(AssignJob(employee, job));
}); });
}, },

@ -598,38 +598,37 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
const server = player.getCurrentServer(); const server = player.getCurrentServer();
return _ctx.helper.hack(server.hostname, true); return _ctx.helper.hack(server.hostname, true);
}, },
installBackdoor: (_ctx: NetscriptContext) => installBackdoor: (_ctx: NetscriptContext) => async (): Promise<void> => {
function (): Promise<void> { _ctx.helper.checkSingularityAccess();
_ctx.helper.checkSingularityAccess(); const baseserver = player.getCurrentServer();
const baseserver = player.getCurrentServer(); if (!(baseserver instanceof Server)) {
if (!(baseserver instanceof Server)) { _ctx.log(() => "cannot backdoor this kind of server");
_ctx.log(() => "cannot backdoor this kind of server"); return Promise.resolve();
return Promise.resolve(); }
const server = baseserver;
const installTime = (calculateHackingTime(server, player) / 4) * 1000;
// No root access or skill level too low
const canHack = netscriptCanHack(server, player);
if (!canHack.res) {
throw _ctx.helper.makeRuntimeErrorMsg(canHack.msg || "");
}
_ctx.log(
() => `Installing backdoor on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(installTime, true)}`,
);
return netscriptDelay(installTime, workerScript).then(function () {
_ctx.log(() => `Successfully installed backdoor on '${server.hostname}'`);
server.backdoorInstalled = true;
if (SpecialServers.WorldDaemon === server.hostname) {
Router.toBitVerse(false, false);
} }
const server = baseserver ; return Promise.resolve();
const installTime = (calculateHackingTime(server, player) / 4) * 1000; });
},
// No root access or skill level too low
const canHack = netscriptCanHack(server, player);
if (!canHack.res) {
throw _ctx.helper.makeRuntimeErrorMsg(canHack.msg || "");
}
_ctx.log(
() => `Installing backdoor on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(installTime, true)}`,
);
return netscriptDelay(installTime, workerScript).then(function () {
_ctx.log(() => `Successfully installed backdoor on '${server.hostname}'`);
server.backdoorInstalled = true;
if (SpecialServers.WorldDaemon === server.hostname) {
Router.toBitVerse(false, false);
}
return Promise.resolve();
});
},
isFocused: (_ctx: NetscriptContext) => isFocused: (_ctx: NetscriptContext) =>
function (): boolean { function (): boolean {
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();

@ -1854,7 +1854,11 @@ export function getNextCompanyPosition(
} }
export function quitJob(this: IPlayer, company: string, _sing = false): void { export function quitJob(this: IPlayer, company: string, _sing = false): void {
if (this.isWorking == true && this.workType !== WorkType.Company && this.companyName == company) { if (
this.isWorking === true &&
[WorkType.Company, WorkType.CompanyPartTime].includes(this.workType) &&
this.companyName === company
) {
this.finishWork(true); this.finishWork(true);
} }
delete this.jobs[company]; delete this.jobs[company];
@ -2287,7 +2291,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
!fulcrumsecrettechonologiesFac.isMember && !fulcrumsecrettechonologiesFac.isMember &&
!fulcrumsecrettechonologiesFac.alreadyInvited && !fulcrumsecrettechonologiesFac.alreadyInvited &&
fulcrumSecretServer.backdoorInstalled && fulcrumSecretServer.backdoorInstalled &&
checkMegacorpRequirements(LocationName.AevumFulcrumTechnologies, 250e3) checkMegacorpRequirements(LocationName.AevumFulcrumTechnologies)
) { ) {
invitedFactions.push(fulcrumsecrettechonologiesFac); invitedFactions.push(fulcrumsecrettechonologiesFac);
} }

@ -3257,7 +3257,7 @@ export interface Bladeburner {
* @remarks * @remarks
* RAM cost: 0 GB * RAM cost: 0 GB
* *
* Returns the amount of accumulated bonus time (seconds) for the Bladeburner mechanic. * Returns the amount of accumulated bonus time (milliseconds) for the Bladeburner mechanic.
* *
* Bonus time is accumulated when the game is offline or if the game is inactive in the browser. * Bonus time is accumulated when the game is offline or if the game is inactive in the browser.
* *
@ -3594,7 +3594,7 @@ export interface Gang {
* @remarks * @remarks
* RAM cost: 0 GB * RAM cost: 0 GB
* *
* Returns the amount of accumulated bonus time (seconds) for the Gang mechanic. * Returns the amount of accumulated bonus time (milliseconds) for the Gang mechanic.
* *
* Bonus time is accumulated when the game is offline or if the game is inactive in the browser. * Bonus time is accumulated when the game is offline or if the game is inactive in the browser.
* *

@ -272,18 +272,20 @@ const Engine: {
const numCyclesOffline = Math.floor(timeOffline / CONSTANTS._idleSpeed); const numCyclesOffline = Math.floor(timeOffline / CONSTANTS._idleSpeed);
// Generate coding contracts // Generate coding contracts
let numContracts = 0; if (Player.sourceFiles.length > 0) {
if (numCyclesOffline < 3000 * 100) { let numContracts = 0;
// if we have less than 100 rolls, just roll them exactly. if (numCyclesOffline < 3000 * 100) {
for (let i = 0; i < numCyclesOffline / 3000; i++) { // if we have less than 100 rolls, just roll them exactly.
if (Math.random() < 0.25) numContracts++; for (let i = 0; i < numCyclesOffline / 3000; i++) {
if (Math.random() < 0.25) numContracts++;
}
} else {
// just average it.
numContracts = (numCyclesOffline / 3000) * 0.25;
}
for (let i = 0; i < numContracts; i++) {
generateRandomContract();
} }
} else {
// just average it.
numContracts = (numCyclesOffline / 3000) * 0.25;
}
for (let i = 0; i < numContracts; i++) {
generateRandomContract();
} }
let offlineReputation = 0; let offlineReputation = 0;

@ -0,0 +1,19 @@
import { TextField, StandardTextFieldProps } from "@mui/material";
import React from "react";
import { numeralWrapper } from "../numeralFormat";
interface IProps extends Omit<StandardTextFieldProps, "onChange"> {
onChange: (v: number) => void;
}
export function NumberInput(props: IProps): React.ReactElement {
const textProps = {
...props,
onChange: (event: React.ChangeEvent<HTMLInputElement>) => {
const amt = numeralWrapper.parseMoney(event.target.value);
if (event.target.value === "" || isNaN(amt)) props.onChange(NaN);
else props.onChange(amt);
},
};
return <TextField {...textProps} />;
}