BUGFIX: Sleeves UI shows and sets wrong task (#1807)

This commit is contained in:
catloversg
2025-01-09 10:06:00 +07:00
committed by GitHub
parent 8c19165323
commit 320c852386
2 changed files with 82 additions and 72 deletions

View File

@@ -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>

View File

@@ -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]);
}