Sleeve work type changes (#412)

This commit is contained in:
Snarling 2023-03-07 18:03:13 -05:00 committed by GitHub
parent 51bf0d1904
commit 401bfe9f31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 142 additions and 226 deletions

@ -25,7 +25,7 @@ import { Factions } from "../../Faction/Factions";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../utils/JSONReviver";
import { formatPercent } from "../../ui/formatNumber";
import { FactionWorkType } from "../../Enums";
import { Work } from "./Work/Work";
import { SleeveWork } from "./Work/Work";
import { SleeveClassWork } from "./Work/SleeveClassWork";
import { ClassType } from "../../Work/ClassWork";
import { SleeveSynchroWork } from "./Work/SleeveSynchroWork";
@ -41,7 +41,7 @@ import { SleevePerson } from "@nsdefs";
import { calculateIntelligenceBonus } from "../formulas/intelligence";
export class Sleeve extends Person implements SleevePerson {
currentWork: Work | null = null;
currentWork: SleeveWork | null = null;
/** Clone retains 'memory' synchronization (and maybe exp?) upon prestige/installing Augs */
memory = 1;
@ -81,7 +81,7 @@ export class Sleeve extends Person implements SleevePerson {
return this.sync / 100;
}
startWork(w: Work): void {
startWork(w: SleeveWork): void {
if (this.currentWork) this.currentWork.finish();
this.currentWork = w;
}
@ -398,6 +398,7 @@ export class Sleeve extends Person implements SleevePerson {
if (!Player.bladeburner) return false;
switch (action) {
case "Field analysis":
case "Field Analysis":
this.startWork(new SleeveBladeburnerWork({ type: "General", name: "Field Analysis" }));
return true;
case "Recruitment":
@ -410,6 +411,7 @@ export class Sleeve extends Person implements SleevePerson {
this.startWork(new SleeveBladeburnerWork({ type: "General", name: "Hyperbolic Regeneration Chamber" }));
return true;
case "Infiltrate synthoids":
case "Infiltrate Synthoids":
this.startWork(new SleeveInfiltrateWork());
return true;
case "Support main sleeve":

@ -15,14 +15,15 @@ export const isSleeveBladeburnerWork = (w: Work | null): w is SleeveBladeburnerW
w !== null && w.type === WorkType.BLADEBURNER;
export class SleeveBladeburnerWork extends Work {
type: WorkType.BLADEBURNER = WorkType.BLADEBURNER;
cyclesWorked = 0;
actionType: "General" | "Contracts";
actionName: string;
constructor(params?: SleeveBladeburnerWorkParams) {
super(WorkType.BLADEBURNER);
super();
this.actionType = params?.type ?? "General";
this.actionName = params?.name ?? "Field analysis";
this.actionName = params?.name ?? "Field Analysis";
}
cyclesNeeded(sleeve: Sleeve): number {

@ -17,11 +17,12 @@ interface ClassWorkParams {
}
export class SleeveClassWork extends Work {
type: WorkType.CLASS = WorkType.CLASS;
classType: ClassType;
location: LocationName;
constructor(params?: ClassWorkParams) {
super(WorkType.CLASS);
super();
this.classType = params?.classType ?? UniversityClassType.computerScience;
this.location = params?.location ?? LocationName.Sector12RothmanUniversity;
}

@ -14,10 +14,11 @@ export const isSleeveCompanyWork = (w: Work | null): w is SleeveCompanyWork =>
w !== null && w.type === WorkType.COMPANY;
export class SleeveCompanyWork extends Work {
type: WorkType.COMPANY = WorkType.COMPANY;
companyName: string;
constructor(companyName?: string) {
super(WorkType.COMPANY);
super();
this.companyName = companyName ?? LocationName.NewTokyoNoodleBar;
}

@ -14,10 +14,11 @@ import { findCrime } from "../../../Crime/CrimeHelpers";
export const isSleeveCrimeWork = (w: Work | null): w is SleeveCrimeWork => w !== null && w.type === WorkType.CRIME;
export class SleeveCrimeWork extends Work {
type: WorkType.CRIME = WorkType.CRIME;
crimeType: CrimeType;
cyclesWorked = 0;
constructor(crimeType?: CrimeType) {
super(WorkType.CRIME);
super();
this.crimeType = crimeType ?? CrimeType.shoplift;
}

@ -19,11 +19,12 @@ export const isSleeveFactionWork = (w: Work | null): w is SleeveFactionWork =>
w !== null && w.type === WorkType.FACTION;
export class SleeveFactionWork extends Work {
type: WorkType.FACTION = WorkType.FACTION;
factionWorkType: FactionWorkType;
factionName: string;
constructor(params?: SleeveFactionWorkParams) {
super(WorkType.FACTION);
super();
this.factionWorkType = params?.factionWorkType ?? FactionWorkType.hacking;
this.factionName = params?.factionName ?? FactionNames.Sector12;
}

@ -10,12 +10,9 @@ export const isSleeveInfiltrateWork = (w: Work | null): w is SleeveInfiltrateWor
w !== null && w.type === WorkType.INFILTRATE;
export class SleeveInfiltrateWork extends Work {
type: WorkType.INFILTRATE = WorkType.INFILTRATE;
cyclesWorked = 0;
constructor() {
super(WorkType.INFILTRATE);
}
cyclesNeeded(): number {
return infiltrateCycles;
}

@ -7,9 +7,7 @@ export const isSleeveRecoveryWork = (w: Work | null): w is SleeveRecoveryWork =>
w !== null && w.type === WorkType.RECOVERY;
export class SleeveRecoveryWork extends Work {
constructor() {
super(WorkType.RECOVERY);
}
type: WorkType.RECOVERY = WorkType.RECOVERY;
process(sleeve: Sleeve, cycles: number) {
sleeve.shock = Math.max(

@ -6,8 +6,9 @@ export const isSleeveSupportWork = (w: Work | null): w is SleeveSupportWork =>
w !== null && w.type === WorkType.SUPPORT;
export class SleeveSupportWork extends Work {
type: WorkType.SUPPORT = WorkType.SUPPORT;
constructor() {
super(WorkType.SUPPORT);
super();
Player.bladeburner?.sleeveSupport(true);
}

@ -8,9 +8,7 @@ export const isSleeveSynchroWork = (w: Work | null): w is SleeveSynchroWork =>
w !== null && w.type === WorkType.SYNCHRO;
export class SleeveSynchroWork extends Work {
constructor() {
super(WorkType.SYNCHRO);
}
type: WorkType.SYNCHRO = WorkType.SYNCHRO;
process(sleeve: Sleeve, cycles: number) {
sleeve.sync = Math.min(

@ -3,6 +3,15 @@ import { IReviverValue } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { applyWorkStatsExp, WorkStats } from "../../../Work/WorkStats";
import { SleeveTask } from "@nsdefs";
import { SleeveCompanyWork } from "./SleeveCompanyWork";
import { SleeveFactionWork } from "./SleeveFactionWork";
import { SleeveCrimeWork } from "./SleeveCrimeWork";
import { SleeveClassWork } from "./SleeveClassWork";
import { SleeveRecoveryWork } from "./SleeveRecoveryWork";
import { SleeveSynchroWork } from "./SleeveSynchroWork";
import { SleeveBladeburnerWork } from "./SleeveBladeburnerWork";
import { SleeveInfiltrateWork } from "./SleeveInfiltrateWork";
import { SleeveSupportWork } from "./SleeveSupportWork";
export const applySleeveGains = (sleeve: Sleeve, shockedStats: WorkStats, mult = 1): void => {
applyWorkStatsExp(sleeve, shockedStats, mult);
@ -15,12 +24,7 @@ export const applySleeveGains = (sleeve: Sleeve, shockedStats: WorkStats, mult =
};
export abstract class Work {
type: WorkType;
constructor(type: WorkType) {
this.type = type;
}
abstract type: WorkType;
abstract process(sleeve: Sleeve, cycles: number): void;
abstract APICopy(sleeve: Sleeve): SleeveTask;
abstract toJSON(): IReviverValue;
@ -40,3 +44,14 @@ export enum WorkType {
INFILTRATE = "INFILTRATE",
SUPPORT = "SUPPORT",
}
export type SleeveWork =
| SleeveCompanyWork
| SleeveFactionWork
| SleeveCrimeWork
| SleeveClassWork
| SleeveRecoveryWork
| SleeveSynchroWork
| SleeveBladeburnerWork
| SleeveInfiltrateWork
| SleeveSupportWork;

@ -11,24 +11,58 @@ import { SleeveAugmentationsModal } from "./SleeveAugmentationsModal";
import { EarningsElement, StatsElement } from "./StatsElement";
import { TaskSelector } from "./TaskSelector";
import { TravelModal } from "./TravelModal";
import { isSleeveClassWork } from "../Work/SleeveClassWork";
import { isSleeveSynchroWork } from "../Work/SleeveSynchroWork";
import { isSleeveRecoveryWork } from "../Work/SleeveRecoveryWork";
import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
import { isSleeveInfiltrateWork } from "../Work/SleeveInfiltrateWork";
import { isSleeveSupportWork } from "../Work/SleeveSupportWork";
import { isSleeveBladeburnerWork } from "../Work/SleeveBladeburnerWork";
import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork";
import { findCrime } from "../../../Crime/CrimeHelpers";
import { CrimeType } from "../../../Enums";
import { WorkType } from "../Work/Work";
interface IProps {
function getWorkDescription(sleeve: Sleeve, progress: number): string {
const work = sleeve.currentWork;
if (!work) return "This sleeve is currently idle.";
switch (work.type) {
case WorkType.COMPANY:
return `This sleeve is currently working your job at ${work.companyName}`;
case WorkType.SUPPORT:
return "This sleeve is currently supporting you in your bladeburner activities.";
case WorkType.CLASS:
return `This sleeve is currently ${work.isGym() ? "working out" : "studying"} at ${work.location}`;
case WorkType.RECOVERY:
return "This sleeve is currently set to focus on shock recovery. This causes the Sleeve's shock to decrease at a faster rate.";
case WorkType.SYNCHRO:
return "This sleeve is currently set to synchronize with the original consciousness. This causes the Sleeve's synchronization to increase.";
case WorkType.BLADEBURNER:
return (
`This sleeve is currently attempting to perform ${work.actionName}.\n\n` +
`Progress: ${formatPercent(progress)}`
);
case WorkType.CRIME:
const crime = work.getCrime();
return (
`This sleeve is currently attempting ${crime.workName} (Success Rate: ${formatPercent(
crime.successRate(sleeve),
)}).\n\n` + `Progress: ${formatPercent(progress)}`
);
case WorkType.FACTION:
// This isn't the way this should be handled...
const workNames = {
[FactionWorkType.field]: "Field Work",
[FactionWorkType.hacking]: "Hacking Contracts",
[FactionWorkType.security]: "Security Work",
};
const doing = workNames[work.factionWorkType] ?? "nothing";
return `This sleeve is currently doing ${doing} for ${work.factionName}.`;
case WorkType.INFILTRATE:
return (
"This sleeve is currently attempting to infiltrate synthoid communities to generate additional contracts and operations.\nThis activity is less efficient the more sleeves are assigned to it.\n\n" +
`Progress: ${formatPercent(progress)}`
);
}
}
interface SleeveElemProps {
sleeve: Sleeve;
rerender: () => void;
}
export function SleeveElem(props: IProps): React.ReactElement {
export function SleeveElem(props: SleeveElemProps): React.ReactElement {
const [statsOpen, setStatsOpen] = useState(false);
const [travelOpen, setTravelOpen] = useState(false);
const [augmentationsOpen, setAugmentationsOpen] = useState(false);
@ -68,88 +102,19 @@ export function SleeveElem(props: IProps): React.ReactElement {
}
props.rerender();
}
let desc = <>This sleeve is currently idle</>;
if (isSleeveCrimeWork(props.sleeve.currentWork)) {
const w = props.sleeve.currentWork;
const crime = w.getCrime();
desc = (
<>
This sleeve is currently attempting {crime.workName} (Success Rate:{" "}
{formatPercent(crime.successRate(props.sleeve))}).
</>
);
let progress = 0;
let percentBar = <></>;
const work = props.sleeve.currentWork;
if (work) {
switch (work.type) {
case WorkType.BLADEBURNER:
case WorkType.CRIME:
case WorkType.INFILTRATE:
progress = work.cyclesWorked / work.cyclesNeeded(props.sleeve);
percentBar = <ProgressBar variant="determinate" value={progress * 100} color="primary" />;
}
if (isSleeveClassWork(props.sleeve.currentWork)) {
if (props.sleeve.currentWork.isGym())
desc = <>This sleeve is currently working out at {props.sleeve.currentWork.location}.</>;
else desc = <>This sleeve is currently studying at {props.sleeve.currentWork.location}.</>;
}
if (isSleeveSynchroWork(props.sleeve.currentWork)) {
desc = (
<>
This sleeve is currently set to synchronize with the original consciousness. This causes the Sleeve's
synchronization to increase.
</>
);
}
if (isSleeveRecoveryWork(props.sleeve.currentWork)) {
desc = (
<>
This sleeve is currently set to focus on shock recovery. This causes the Sleeve's shock to decrease at a faster
rate.
</>
);
}
if (isSleeveFactionWork(props.sleeve.currentWork)) {
let doing = "nothing";
switch (props.sleeve.currentWork.factionWorkType) {
case FactionWorkType.field:
doing = "Field work";
break;
case FactionWorkType.hacking:
doing = "Hacking contracts";
break;
case FactionWorkType.security:
doing = "Security work";
break;
}
desc = (
<>
This sleeve is currently doing {doing} for {props.sleeve.currentWork.factionName}.
</>
);
}
if (isSleeveCompanyWork(props.sleeve.currentWork)) {
desc = <>This sleeve is currently working your job at {props.sleeve.currentWork.companyName}.</>;
}
if (isSleeveBladeburnerWork(props.sleeve.currentWork)) {
const w = props.sleeve.currentWork;
desc = (
<>
This sleeve is currently attempting to perform {w.actionName}. (
{((100 * w.cyclesWorked) / w.cyclesNeeded(props.sleeve)).toFixed(2)}%)
</>
);
}
if (isSleeveInfiltrateWork(props.sleeve.currentWork)) {
const w = props.sleeve.currentWork;
desc = (
<>
This sleeve is currently attempting to infiltrate synthoids communities. (
{((100 * w.cyclesWorked) / w.cyclesNeeded()).toFixed(2)}%)
</>
);
}
if (isSleeveSupportWork(props.sleeve.currentWork)) {
desc = <>This sleeve is currently supporting you in your bladeburner activities.</>;
}
const desc = getWorkDescription(props.sleeve, progress);
return (
<>
<Paper sx={{ p: 1, display: "grid", gridTemplateColumns: "1fr 1fr", width: "auto", gap: 1 }}>
@ -189,25 +154,8 @@ export function SleeveElem(props: IProps): React.ReactElement {
<Button onClick={setTask} sx={{ width: "100%" }}>
Set Task
</Button>
<Typography>{desc}</Typography>
<Typography>
{isSleeveCrimeWork(props.sleeve.currentWork) && (
<ProgressBar
variant="determinate"
value={(props.sleeve.currentWork.cyclesWorked / props.sleeve.currentWork.cyclesNeeded()) * 100}
color="primary"
/>
)}
{isSleeveBladeburnerWork(props.sleeve.currentWork) && (
<ProgressBar
variant="determinate"
value={
(props.sleeve.currentWork.cyclesWorked / props.sleeve.currentWork.cyclesNeeded(props.sleeve)) * 100
}
color="primary"
/>
)}
</Typography>
<Typography whiteSpace={"pre-wrap"}>{desc}</Typography>
{percentBar}
</span>
</Paper>
<MoreStatsModal open={statsOpen} onClose={() => setStatsOpen(false)} sleeve={props.sleeve} />

@ -10,17 +10,12 @@ import { FactionNames } from "../../../Faction/data/FactionNames";
import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
import { isSleeveBladeburnerWork } from "../Work/SleeveBladeburnerWork";
import { isSleeveRecoveryWork } from "../Work/SleeveRecoveryWork";
import { isSleeveSynchroWork } from "../Work/SleeveSynchroWork";
import { isSleeveClassWork } from "../Work/SleeveClassWork";
import { isSleeveInfiltrateWork } from "../Work/SleeveInfiltrateWork";
import { isSleeveSupportWork } from "../Work/SleeveSupportWork";
import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork";
import { CrimeType, FactionWorkType, GymType, UniversityClassType } from "../../../Enums";
import { CrimeType, FactionWorkType, GymType } from "../../../Enums";
import { checkEnum } from "../../../utils/helpers/enum";
import { WorkType } from "../Work/Work";
const universitySelectorOptions: string[] = [
"Study Computer Science",
"Computer Science",
"Data Structures",
"Networks",
"Algorithms",
@ -31,11 +26,11 @@ const universitySelectorOptions: string[] = [
const gymSelectorOptions: string[] = ["Train Strength", "Train Defense", "Train Dexterity", "Train Agility"];
const bladeburnerSelectorOptions: string[] = [
"Field analysis",
"Field Analysis",
"Recruitment",
"Diplomacy",
"Hyperbolic Regeneration Chamber",
"Infiltrate synthoids",
"Infiltrate Synthoids",
"Support main sleeve",
"Take on contracts",
];
@ -249,86 +244,43 @@ const canDo: {
};
function getABC(sleeve: Sleeve): [string, string, string] {
const w = sleeve.currentWork;
if (w === null) {
return ["------", "------", "------"];
const work = sleeve.currentWork;
if (work === null) return ["------", "------", "------"];
switch (work.type) {
case WorkType.COMPANY:
return ["Work for Company", work.companyName, "------"];
case WorkType.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 WorkType.BLADEBURNER:
if (work.actionType === "Contracts") {
return ["Perform Bladeburner Actions", "Take on contracts", work.actionName];
}
if (isSleeveCompanyWork(w)) {
return ["Work for Company", w.companyName, "------"];
}
if (isSleeveFactionWork(w)) {
let workType = "";
switch (w.factionWorkType) {
case FactionWorkType.hacking:
workType = "Hacking Contracts";
break;
case FactionWorkType.field:
workType = "Field Work";
break;
case FactionWorkType.security:
workType = "Security Work";
break;
}
return ["Work for Faction", w.factionName, workType];
}
if (isSleeveBladeburnerWork(w)) {
if (w.actionType === "Contracts") {
return ["Perform Bladeburner Actions", "Take on contracts", w.actionName];
}
switch (w.actionName) {
case "Field Analysis":
return ["Perform Bladeburner Actions", "Field Analysis", "------"];
case "Diplomacy":
return ["Perform Bladeburner Actions", "Diplomacy", "------"];
case "Recruitment":
return ["Perform Bladeburner Actions", "Recruitment", "------"];
case "Hyperbolic Regeneration Chamber":
return ["Perform Bladeburner Actions", "Hyperbolic Regeneration Chamber", "------"];
}
}
if (isSleeveClassWork(w)) {
switch (w.classType) {
case UniversityClassType.computerScience:
return ["Take University Course", "Study Computer Science", w.location];
case UniversityClassType.dataStructures:
return ["Take University Course", "Data Structures", w.location];
case UniversityClassType.networks:
return ["Take University Course", "Networks", w.location];
case UniversityClassType.algorithms:
return ["Take University Course", "Algorithms", w.location];
case UniversityClassType.management:
return ["Take University Course", "Management", w.location];
case UniversityClassType.leadership:
return ["Take University Course", "Leadership", w.location];
case GymType.strength:
return ["Workout at Gym", "Train Strength", w.location];
case GymType.defense:
return ["Workout at Gym", "Train Defense", w.location];
case GymType.dexterity:
return ["Workout at Gym", "Train Dexterity", w.location];
case GymType.agility:
return ["Workout at Gym", "Train Agility", w.location];
}
}
if (isSleeveCrimeWork(w)) {
return ["Commit Crime", checkEnum(CrimeType, w.crimeType) ? w.crimeType : "Shoplift", "------"];
}
if (isSleeveSupportWork(w)) {
return ["Perform Bladeburner Actions", work.actionName, "------"];
case WorkType.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 WorkType.CRIME:
return ["Commit Crime", checkEnum(CrimeType, work.crimeType) ? work.crimeType : "Shoplift", "------"];
case WorkType.SUPPORT:
return ["Perform Bladeburner Actions", "Support main sleeve", "------"];
}
if (isSleeveInfiltrateWork(w)) {
return ["Perform Bladeburner Actions", "Infiltrate synthoids", "------"];
}
if (isSleeveRecoveryWork(w)) {
case WorkType.INFILTRATE:
return ["Perform Bladeburner Actions", "Infiltrate Synthoids", "------"];
case WorkType.RECOVERY:
return ["Shock Recovery", "------", "------"];
}
if (isSleeveSynchroWork(w)) {
case WorkType.SYNCHRO:
return ["Synchronize", "------", "------"];
}
return ["------", "------", "------"];
}
export function TaskSelector(props: IProps): React.ReactElement {