mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 06:18:42 +02:00
BUGFIX: Sleeves UI shows and sets wrong task (#1807)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Box, Button, Paper, Tooltip, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import { CrimeType, FactionWorkType } from "@enums";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { BladeburnerActionType, CrimeType, FactionWorkType, GymType } from "@enums";
|
||||
import { CONSTANTS } from "../../../Constants";
|
||||
import { Player } from "@player";
|
||||
import { formatPercent, formatInt } from "../../../ui/formatNumber";
|
||||
@@ -12,7 +12,7 @@ import { EarningsElement, StatsElement } from "./StatsElement";
|
||||
import { TaskSelector } from "./TaskSelector";
|
||||
import { TravelModal } from "./TravelModal";
|
||||
import { findCrime } from "../../../Crime/CrimeHelpers";
|
||||
import { SleeveWorkType } from "../Work/Work";
|
||||
import { type SleeveWork, SleeveWorkType } from "../Work/Work";
|
||||
import { getEnumHelper } from "../../../utils/EnumHelper";
|
||||
|
||||
function getWorkDescription(sleeve: Sleeve, progress: number): string {
|
||||
@@ -74,6 +74,51 @@ function getWorkDescription(sleeve: Sleeve, progress: number): string {
|
||||
}
|
||||
}
|
||||
|
||||
function calculateABC(work: SleeveWork | null): [string, string, string] {
|
||||
if (work === null) {
|
||||
return ["Idle", "------", "------"];
|
||||
}
|
||||
switch (work.type) {
|
||||
case SleeveWorkType.COMPANY:
|
||||
return ["Work for Company", work.companyName, "------"];
|
||||
case SleeveWorkType.FACTION: {
|
||||
const workNames = {
|
||||
[FactionWorkType.field]: "Field Work",
|
||||
[FactionWorkType.hacking]: "Hacking Contracts",
|
||||
[FactionWorkType.security]: "Security Work",
|
||||
};
|
||||
return ["Work for Faction", work.factionName, workNames[work.factionWorkType] ?? ""];
|
||||
}
|
||||
case SleeveWorkType.BLADEBURNER:
|
||||
if (work.actionId.type === BladeburnerActionType.Contract) {
|
||||
return ["Perform Bladeburner Actions", "Take on contracts", work.actionId.name];
|
||||
}
|
||||
return ["Perform Bladeburner Actions", work.actionId.name, "------"];
|
||||
case SleeveWorkType.CLASS: {
|
||||
if (!work.isGym()) {
|
||||
return ["Take University Course", work.classType, work.location];
|
||||
}
|
||||
const gymNames: Record<GymType, string> = {
|
||||
[GymType.strength]: "Train Strength",
|
||||
[GymType.defense]: "Train Defense",
|
||||
[GymType.dexterity]: "Train Dexterity",
|
||||
[GymType.agility]: "Train Agility",
|
||||
};
|
||||
return ["Workout at Gym", gymNames[work.classType as GymType], work.location];
|
||||
}
|
||||
case SleeveWorkType.CRIME:
|
||||
return ["Commit Crime", getEnumHelper("CrimeType").getMember(work.crimeType, { alwaysMatch: true }), "------"];
|
||||
case SleeveWorkType.SUPPORT:
|
||||
return ["Perform Bladeburner Actions", "Support main sleeve", "------"];
|
||||
case SleeveWorkType.INFILTRATE:
|
||||
return ["Perform Bladeburner Actions", "Infiltrate Synthoids", "------"];
|
||||
case SleeveWorkType.RECOVERY:
|
||||
return ["Shock Recovery", "------", "------"];
|
||||
case SleeveWorkType.SYNCHRO:
|
||||
return ["Synchronize", "------", "------"];
|
||||
}
|
||||
}
|
||||
|
||||
interface SleeveElemProps {
|
||||
sleeve: Sleeve;
|
||||
rerender: () => void;
|
||||
@@ -83,7 +128,19 @@ export function SleeveElem(props: SleeveElemProps): React.ReactElement {
|
||||
const [travelOpen, setTravelOpen] = useState(false);
|
||||
const [augmentationsOpen, setAugmentationsOpen] = useState(false);
|
||||
|
||||
const [abc, setABC] = useState(["Idle", "------", "------"]);
|
||||
/**
|
||||
* "abc" contains values of 3 dropdown inputs. It will be set when:
|
||||
* - The player selects a task and its options.
|
||||
* - The sleeve's current task is set by non-UI things (e.g., NS API).
|
||||
*/
|
||||
const [abc, setABC] = useState(calculateABC(props.sleeve.currentWork));
|
||||
|
||||
/**
|
||||
* Update abc if the sleeve's current task is set by non-UI things.
|
||||
*/
|
||||
useEffect(() => {
|
||||
setABC(calculateABC(props.sleeve.currentWork));
|
||||
}, [props.sleeve.currentWork]);
|
||||
|
||||
function setTask(): void {
|
||||
switch (abc[0]) {
|
||||
@@ -169,7 +226,7 @@ export function SleeveElem(props: SleeveElemProps): React.ReactElement {
|
||||
</span>
|
||||
<span>
|
||||
<EarningsElement sleeve={props.sleeve} />
|
||||
<TaskSelector sleeve={props.sleeve} setABC={setABC} />
|
||||
<TaskSelector sleeve={props.sleeve} abc={abc} setABC={setABC} />
|
||||
<Button onClick={setTask} sx={{ width: "100%" }}>
|
||||
Set Task
|
||||
</Button>
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import type { Sleeve } from "../Sleeve";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import { MenuItem, Select, SelectChangeEvent } from "@mui/material";
|
||||
|
||||
import { Player } from "@player";
|
||||
import {
|
||||
BladeburnerActionType,
|
||||
BladeburnerContractName,
|
||||
CityName,
|
||||
FactionName,
|
||||
FactionWorkType,
|
||||
GymType,
|
||||
LocationName,
|
||||
} from "@enums";
|
||||
import { BladeburnerActionType, BladeburnerContractName, CityName, FactionName, LocationName } from "@enums";
|
||||
import { Crimes } from "../../../Crime/Crimes";
|
||||
import { Factions } from "../../../Faction/Factions";
|
||||
import { getEnumHelper } from "../../../utils/EnumHelper";
|
||||
@@ -42,7 +34,8 @@ const bladeburnerSelectorOptions: string[] = [
|
||||
|
||||
interface IProps {
|
||||
sleeve: Sleeve;
|
||||
setABC: (abc: string[]) => void;
|
||||
abc: [string, string, string];
|
||||
setABC: (abc: [string, string, string]) => void;
|
||||
}
|
||||
|
||||
interface ITaskDetails {
|
||||
@@ -247,89 +240,49 @@ const canDo: {
|
||||
Synchronize: (sleeve: Sleeve) => sleeve.sync < 100,
|
||||
};
|
||||
|
||||
function getABC(sleeve: Sleeve): [string, string, string] {
|
||||
const work = sleeve.currentWork;
|
||||
if (work === null) return ["Idle", "------", "------"];
|
||||
switch (work.type) {
|
||||
case SleeveWorkType.COMPANY:
|
||||
return ["Work for Company", work.companyName, "------"];
|
||||
case SleeveWorkType.FACTION: {
|
||||
const workNames = {
|
||||
[FactionWorkType.field]: "Field Work",
|
||||
[FactionWorkType.hacking]: "Hacking Contracts",
|
||||
[FactionWorkType.security]: "Security Work",
|
||||
};
|
||||
return ["Work for Faction", work.factionName, workNames[work.factionWorkType] ?? ""];
|
||||
}
|
||||
case SleeveWorkType.BLADEBURNER:
|
||||
if (work.actionId.type === BladeburnerActionType.Contract) {
|
||||
return ["Perform Bladeburner Actions", "Take on contracts", work.actionId.name];
|
||||
}
|
||||
return ["Perform Bladeburner Actions", work.actionId.name, "------"];
|
||||
case SleeveWorkType.CLASS: {
|
||||
if (!work.isGym()) return ["Take University Course", work.classType, work.location];
|
||||
const gymNames: Record<GymType, string> = {
|
||||
[GymType.strength]: "Train Strength",
|
||||
[GymType.defense]: "Train Defense",
|
||||
[GymType.dexterity]: "Train Dexterity",
|
||||
[GymType.agility]: "Train Agility",
|
||||
};
|
||||
return ["Workout at Gym", gymNames[work.classType as GymType], work.location];
|
||||
}
|
||||
case SleeveWorkType.CRIME:
|
||||
return ["Commit Crime", getEnumHelper("CrimeType").getMember(work.crimeType, { alwaysMatch: true }), "------"];
|
||||
case SleeveWorkType.SUPPORT:
|
||||
return ["Perform Bladeburner Actions", "Support main sleeve", "------"];
|
||||
case SleeveWorkType.INFILTRATE:
|
||||
return ["Perform Bladeburner Actions", "Infiltrate Synthoids", "------"];
|
||||
case SleeveWorkType.RECOVERY:
|
||||
return ["Shock Recovery", "------", "------"];
|
||||
case SleeveWorkType.SYNCHRO:
|
||||
return ["Synchronize", "------", "------"];
|
||||
}
|
||||
}
|
||||
|
||||
export function TaskSelector(props: IProps): React.ReactElement {
|
||||
const abc = getABC(props.sleeve);
|
||||
const [s0, setS0] = useState(abc[0]);
|
||||
const [s1, setS1] = useState(abc[1]);
|
||||
const [s2, setS2] = useState(abc[2]);
|
||||
const s0 = props.abc[0];
|
||||
const s1 = props.abc[1];
|
||||
const s2 = props.abc[2];
|
||||
|
||||
const validActions = Object.keys(canDo).filter((k) => (canDo[k] as (sleeve: Sleeve) => boolean)(props.sleeve));
|
||||
const validActions = Object.keys(canDo).filter((taskType) => {
|
||||
const canDoTask = canDo[taskType];
|
||||
if (canDoTask === undefined) {
|
||||
return false;
|
||||
}
|
||||
return canDoTask(props.sleeve);
|
||||
});
|
||||
|
||||
const detailsF = tasks[s0];
|
||||
if (detailsF === undefined) throw new Error(`No function for task '${s0}'`);
|
||||
if (detailsF === undefined) {
|
||||
throw new Error(`No function for task '${s0}'`);
|
||||
}
|
||||
const details = detailsF(props.sleeve);
|
||||
const details2 = details.second(s1);
|
||||
|
||||
if (details.first.length > 0 && !details.first.includes(s1)) {
|
||||
setS1(details.first[0]);
|
||||
props.setABC([s0, details.first[0], s2]);
|
||||
}
|
||||
if (details2.length > 0 && !details2.includes(s2)) {
|
||||
setS2(details2[0]);
|
||||
props.setABC([s0, s1, details2[0]]);
|
||||
}
|
||||
|
||||
function onS0Change(event: SelectChangeEvent): void {
|
||||
const n = event.target.value;
|
||||
const detailsF = tasks[n];
|
||||
if (detailsF === undefined) throw new Error(`No function for task '${s0}'`);
|
||||
if (detailsF === undefined) {
|
||||
throw new Error(`No function for task '${s0}'`);
|
||||
}
|
||||
const details = detailsF(props.sleeve);
|
||||
const details2 = details.second(details.first[0]) ?? ["------"];
|
||||
setS2(details2[0]);
|
||||
setS1(details.first[0]);
|
||||
setS0(n);
|
||||
props.setABC([n, details.first[0], details2[0]]);
|
||||
}
|
||||
|
||||
function onS1Change(event: SelectChangeEvent): void {
|
||||
setS1(event.target.value);
|
||||
props.setABC([s0, event.target.value, s2]);
|
||||
}
|
||||
|
||||
function onS2Change(event: SelectChangeEvent): void {
|
||||
setS2(event.target.value);
|
||||
props.setABC([s0, s1, event.target.value]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user