mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-25 10:42:51 +02:00
Convert sleeves to react, fix shock recovery bug
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
import * as React from "react";
|
||||
|
||||
export function EarningsTableElement(title: string, stats: any[][]): React.ReactElement {
|
||||
interface IProps {
|
||||
title: string;
|
||||
stats: any[][];
|
||||
}
|
||||
|
||||
export function EarningsTableElement(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<pre>{title}</pre>
|
||||
<pre>{props.title}</pre>
|
||||
<table>
|
||||
<tbody>
|
||||
{stats.map((stat: any[], i: number) => (
|
||||
{props.stats.map((stat: any[], i: number) => (
|
||||
<tr key={i}>
|
||||
{stat.map((s: any, i: number) => {
|
||||
let style = {};
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { SleeveTaskType } from "../SleeveTaskTypesEnum";
|
||||
import { SleeveFaq } from "../data/SleeveFaq";
|
||||
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
|
||||
import { Faction } from "../../../Faction/Faction";
|
||||
import { Factions } from "../../../Faction/Factions";
|
||||
import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum";
|
||||
|
||||
import { Crime } from "../../../Crime/Crime";
|
||||
import { Crimes } from "../../../Crime/Crimes";
|
||||
import { CityName } from "../../../Locations/data/CityNames";
|
||||
import { LocationName } from "../../../Locations/data/LocationNames";
|
||||
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import { Page, routing } from "../../../ui/navigationTracking";
|
||||
|
||||
import { dialogBoxCreate } from "../../../../utils/DialogBox";
|
||||
|
||||
import { createProgressBarText } from "../../../../utils/helpers/createProgressBarText";
|
||||
import { exceptionAlert } from "../../../../utils/helpers/exceptionAlert";
|
||||
|
||||
import { clearEventListeners } from "../../../../utils/uiHelpers/clearEventListeners";
|
||||
import { createElement } from "../../../../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../../../../utils/uiHelpers/createOptionElement";
|
||||
import { createPopup } from "../../../ui/React/createPopup";
|
||||
import { getSelectValue } from "../../../../utils/uiHelpers/getSelectData";
|
||||
import { removeChildrenFromElement } from "../../../../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { removeElement } from "../../../../utils/uiHelpers/removeElement";
|
||||
|
||||
import { SleeveAugmentationsPopup } from "../ui/SleeveAugmentationsPopup";
|
||||
import { TravelPopup } from "../ui/TravelPopup";
|
||||
import { EarningsTableElement } from "../ui/EarningsTableElement";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import { MoneyRate } from "../../../ui/React/MoneyRate";
|
||||
import { ReputationRate } from "../../../ui/React/ReputationRate";
|
||||
import { StatsElement } from "../ui/StatsElement";
|
||||
import { MoreStatsContent } from "../ui/MoreStatsContent";
|
||||
import { MoreEarningsContent } from "../ui/MoreEarningsContent";
|
||||
import { TaskSelector } from "../ui/TaskSelector";
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
sleeve: Sleeve;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
export function SleeveElem(props: IProps): React.ReactElement {
|
||||
const [abc, setABC] = useState(["------", "------", "------"]);
|
||||
|
||||
function openMoreStats(): void {
|
||||
dialogBoxCreate(<MoreStatsContent sleeve={props.sleeve} />);
|
||||
}
|
||||
|
||||
function openTravel(): void {
|
||||
const popupId = "sleeve-travel-popup";
|
||||
createPopup(popupId, TravelPopup, {
|
||||
popupId: popupId,
|
||||
sleeve: props.sleeve,
|
||||
player: props.player,
|
||||
rerender: props.rerender,
|
||||
});
|
||||
}
|
||||
|
||||
function openManageAugmentations(): void {
|
||||
const popupId = "sleeve-augmentation-popup";
|
||||
createPopup(popupId, SleeveAugmentationsPopup, {
|
||||
sleeve: props.sleeve,
|
||||
player: props.player,
|
||||
});
|
||||
}
|
||||
|
||||
function openMoreEarnings(): void {
|
||||
dialogBoxCreate(<MoreEarningsContent sleeve={props.sleeve} />);
|
||||
}
|
||||
|
||||
function setTask(): void {
|
||||
props.sleeve.resetTaskStatus(); // sets to idle
|
||||
let res;
|
||||
switch (abc[0]) {
|
||||
case "------":
|
||||
break;
|
||||
case "Work for Company":
|
||||
res = props.sleeve.workForCompany(props.player, abc[1]);
|
||||
break;
|
||||
case "Work for Faction":
|
||||
res = props.sleeve.workForFaction(props.player, abc[1], abc[2]);
|
||||
break;
|
||||
case "Commit Crime":
|
||||
res = props.sleeve.commitCrime(props.player, abc[1]);
|
||||
break;
|
||||
case "Take University Course":
|
||||
res = props.sleeve.takeUniversityCourse(props.player, abc[2], abc[1]);
|
||||
break;
|
||||
case "Workout at Gym":
|
||||
res = props.sleeve.workoutAtGym(props.player, abc[2], abc[1]);
|
||||
break;
|
||||
case "Shock Recovery":
|
||||
res = props.sleeve.shockRecovery(props.player);
|
||||
break;
|
||||
case "Synchronize":
|
||||
res = props.sleeve.synchronize(props.player);
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid/Unrecognized taskValue in setSleeveTask(): ${abc[0]}`);
|
||||
}
|
||||
props.rerender();
|
||||
}
|
||||
|
||||
let desc = <></>;
|
||||
switch (props.sleeve.currentTask) {
|
||||
case SleeveTaskType.Idle:
|
||||
desc = <>This sleeve is currently idle</>;
|
||||
break;
|
||||
case SleeveTaskType.Company:
|
||||
desc = <>This sleeve is currently working your job at ${props.sleeve.currentTaskLocation}.</>;
|
||||
break;
|
||||
case SleeveTaskType.Faction:
|
||||
desc = (
|
||||
<>
|
||||
This sleeve is currently doing ${props.sleeve.factionWorkType} for ${props.sleeve.currentTaskLocation}.
|
||||
</>
|
||||
);
|
||||
break;
|
||||
case SleeveTaskType.Crime:
|
||||
desc = (
|
||||
<>
|
||||
This sleeve is currently attempting to {Crimes[props.sleeve.crimeType].type} (Success Rate:{" "}
|
||||
{numeralWrapper.formatPercentage(Crimes[props.sleeve.crimeType].successRate(props.sleeve))}).
|
||||
</>
|
||||
);
|
||||
break;
|
||||
case SleeveTaskType.Class:
|
||||
desc = <>This sleeve is currently studying/taking a course at {props.sleeve.currentTaskLocation}.</>;
|
||||
break;
|
||||
case SleeveTaskType.Gym:
|
||||
desc = <>This sleeve is currently working out at {props.sleeve.currentTaskLocation}.</>;
|
||||
break;
|
||||
case SleeveTaskType.Recovery:
|
||||
desc = (
|
||||
<>
|
||||
This sleeve is currently set to focus on shock recovery. This causes the Sleeve's shock to decrease at a
|
||||
faster rate.
|
||||
</>
|
||||
);
|
||||
break;
|
||||
case SleeveTaskType.Synchro:
|
||||
desc = (
|
||||
<>
|
||||
This sleeve is currently set to synchronize with the original consciousness. This causes the Sleeve's
|
||||
synchronization to increase.
|
||||
</>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid/Unrecognized taskValue in updateSleeveTaskDescription(): ${abc[0]}`);
|
||||
}
|
||||
|
||||
let data: any[][] = [];
|
||||
if (props.sleeve.currentTask === SleeveTaskType.Crime) {
|
||||
data = [
|
||||
[`Money`, <Money money={parseFloat(props.sleeve.currentTaskLocation)} />, `(on success)`],
|
||||
[`Hacking Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.hack), `(2x on success)`],
|
||||
[`Strength Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.str), `(2x on success)`],
|
||||
[`Defense Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.def), `(2x on success)`],
|
||||
[`Dexterity Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.dex), `(2x on success)`],
|
||||
[`Agility Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.agi), `(2x on success)`],
|
||||
[`Charisma Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.cha), `(2x on success)`],
|
||||
];
|
||||
|
||||
// elems.taskProgressBar.innerText = createProgressBarText({
|
||||
// progress: props.sleeve.currentTaskTime / props.sleeve.currentTaskMaxTime,
|
||||
// totalTicks: 25,
|
||||
// });
|
||||
} else {
|
||||
data = [
|
||||
[`Money:`, MoneyRate(5 * props.sleeve.gainRatesForTask.money)],
|
||||
[`Hacking Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.hack)} / s`],
|
||||
[`Strength Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.str)} / s`],
|
||||
[`Defense Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.def)} / s`],
|
||||
[`Dexterity Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.dex)} / s`],
|
||||
[`Agility Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.agi)} / s`],
|
||||
[`Charisma Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.cha)} / s`],
|
||||
];
|
||||
if (props.sleeve.currentTask === SleeveTaskType.Company || props.sleeve.currentTask === SleeveTaskType.Faction) {
|
||||
const repGain: number = props.sleeve.getRepGain(props.player);
|
||||
data.push([`Reputation:`, ReputationRate(5 * repGain)]);
|
||||
}
|
||||
|
||||
// elems.taskProgressBar.innerText = "";
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sleeve-elem">
|
||||
<div className="sleeve-panel" style={{ width: "25%" }}>
|
||||
<div className="sleeve-stats-text">
|
||||
<StatsElement sleeve={props.sleeve} />
|
||||
<button className="std-button" onClick={openMoreStats}>
|
||||
More Stats
|
||||
</button>
|
||||
<button className="std-button" onClick={openTravel}>
|
||||
Travel
|
||||
</button>
|
||||
<button
|
||||
className={`std-button${props.sleeve.shock < 100 ? " tooltip" : ""}`}
|
||||
onClick={openManageAugmentations}
|
||||
style={{ display: "block" }}
|
||||
disabled={props.sleeve.shock < 100}
|
||||
>
|
||||
Manage Augmentations
|
||||
{props.sleeve.shock < 100 && <span className="tooltiptext">Unlocked when sleeve has fully recovered</span>}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sleeve-panel" style={{ width: "40%" }}>
|
||||
<TaskSelector player={props.player} sleeve={props.sleeve} setABC={setABC} />
|
||||
<p>{desc}</p>
|
||||
<p>
|
||||
{props.sleeve.currentTask === SleeveTaskType.Crime &&
|
||||
createProgressBarText({
|
||||
progress: props.sleeve.currentTaskTime / props.sleeve.currentTaskMaxTime,
|
||||
totalTicks: 25,
|
||||
})}
|
||||
</p>
|
||||
<button className="std-button" onClick={setTask}>
|
||||
Set Task
|
||||
</button>
|
||||
</div>
|
||||
<div className="sleeve-panel" style={{ width: "35%" }}>
|
||||
<EarningsTableElement title="Earnings (Pre-Synchronization)" stats={data} />
|
||||
<button className="std-button" onClick={openMoreEarnings}>
|
||||
More Earnings Info
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { SleeveTaskType } from "../SleeveTaskTypesEnum";
|
||||
import { SleeveFaq } from "../data/SleeveFaq";
|
||||
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
|
||||
import { Faction } from "../../../Faction/Faction";
|
||||
import { Factions } from "../../../Faction/Factions";
|
||||
import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum";
|
||||
|
||||
import { Crime } from "../../../Crime/Crime";
|
||||
import { Crimes } from "../../../Crime/Crimes";
|
||||
import { CityName } from "../../../Locations/data/CityNames";
|
||||
import { LocationName } from "../../../Locations/data/LocationNames";
|
||||
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import { Page, routing } from "../../../ui/navigationTracking";
|
||||
|
||||
import { dialogBoxCreate } from "../../../../utils/DialogBox";
|
||||
|
||||
import { createProgressBarText } from "../../../../utils/helpers/createProgressBarText";
|
||||
import { exceptionAlert } from "../../../../utils/helpers/exceptionAlert";
|
||||
|
||||
import { clearEventListeners } from "../../../../utils/uiHelpers/clearEventListeners";
|
||||
import { createElement } from "../../../../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../../../../utils/uiHelpers/createOptionElement";
|
||||
import { createPopup } from "../../../ui/React/createPopup";
|
||||
import { getSelectValue } from "../../../../utils/uiHelpers/getSelectData";
|
||||
import { removeChildrenFromElement } from "../../../../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { removeElement } from "../../../../utils/uiHelpers/removeElement";
|
||||
|
||||
import { SleeveAugmentationsPopup } from "../ui/SleeveAugmentationsPopup";
|
||||
import { TravelPopup } from "../ui/TravelPopup";
|
||||
import { EarningsTableElement } from "../ui/EarningsTableElement";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import { MoneyRate } from "../../../ui/React/MoneyRate";
|
||||
import { ReputationRate } from "../../../ui/React/ReputationRate";
|
||||
import { StatsElement } from "../ui/StatsElement";
|
||||
import { MoreStatsContent } from "../ui/MoreStatsContent";
|
||||
import { MoreEarningsContent } from "../ui/MoreEarningsContent";
|
||||
import { SleeveElem } from "../ui/SleeveElem";
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
export function SleeveRoot(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(rerender, 150);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{ width: "70%" }}>
|
||||
<h1>Sleeves</h1>
|
||||
<p>
|
||||
Duplicate Sleeves are MK-V Synthoids (synthetic androids) into which your consciousness has been copied. In
|
||||
other words, these Synthoids contain a perfect duplicate of your mind.
|
||||
<br />
|
||||
<br />
|
||||
Sleeves can be used to perform different tasks synchronously.
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
|
||||
<button className="std-button" style={{ display: "inline-block" }}>
|
||||
FAQ
|
||||
</button>
|
||||
<a
|
||||
className="std-button"
|
||||
style={{ display: "inline-block" }}
|
||||
target="_blank"
|
||||
href="https://bitburner.readthedocs.io/en/latest/advancedgameplay/sleeves.html#duplicate-sleeves"
|
||||
>
|
||||
Documentation
|
||||
</a>
|
||||
<ul>
|
||||
{props.player.sleeves.map((sleeve, i) => (
|
||||
<li key={i}>
|
||||
<SleeveElem rerender={rerender} player={props.player} sleeve={sleeve} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,11 @@ import { Sleeve } from "../Sleeve";
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import * as React from "react";
|
||||
|
||||
export function StatsElement(sleeve: Sleeve): React.ReactElement {
|
||||
interface IProps {
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
export function StatsElement(props: IProps): React.ReactElement {
|
||||
let style = {};
|
||||
style = { textAlign: "right" };
|
||||
return (
|
||||
@@ -12,65 +16,65 @@ export function StatsElement(sleeve: Sleeve): React.ReactElement {
|
||||
<tr>
|
||||
<td className="character-hp-cell">HP: </td>
|
||||
<td className="character-hp-cell" style={style}>
|
||||
{numeralWrapper.formatHp(sleeve.hp)} / {numeralWrapper.formatHp(sleeve.max_hp)}
|
||||
{numeralWrapper.formatHp(props.sleeve.hp)} / {numeralWrapper.formatHp(props.sleeve.max_hp)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>City: </td>
|
||||
<td style={style}>{sleeve.city}</td>
|
||||
<td style={style}>{props.sleeve.city}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-hack-cell">Hacking: </td>
|
||||
<td className="character-hack-cell" style={style}>
|
||||
{numeralWrapper.formatSkill(sleeve.hacking_skill)}
|
||||
{numeralWrapper.formatSkill(props.sleeve.hacking_skill)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-combat-cell">Strength: </td>
|
||||
<td className="character-combat-cell" style={style}>
|
||||
{numeralWrapper.formatSkill(sleeve.strength)}
|
||||
{numeralWrapper.formatSkill(props.sleeve.strength)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-combat-cell">Defense: </td>
|
||||
<td className="character-combat-cell" style={style}>
|
||||
{numeralWrapper.formatSkill(sleeve.defense)}
|
||||
{numeralWrapper.formatSkill(props.sleeve.defense)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-combat-cell">Dexterity: </td>
|
||||
<td className="character-combat-cell" style={style}>
|
||||
{numeralWrapper.formatSkill(sleeve.dexterity)}
|
||||
{numeralWrapper.formatSkill(props.sleeve.dexterity)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-combat-cell">Agility: </td>
|
||||
<td className="character-combat-cell" style={style}>
|
||||
{numeralWrapper.formatSkill(sleeve.agility)}
|
||||
{numeralWrapper.formatSkill(props.sleeve.agility)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-cha-cell">Charisma: </td>
|
||||
<td className="character-cha-cell" style={style}>
|
||||
{numeralWrapper.formatSkill(sleeve.charisma)}
|
||||
{numeralWrapper.formatSkill(props.sleeve.charisma)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-int-cell">Shock: </td>
|
||||
<td className="character-int-cell" style={style}>
|
||||
{numeralWrapper.formatSleeveShock(100 - sleeve.shock)}
|
||||
{numeralWrapper.formatSleeveShock(100 - props.sleeve.shock)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-int-cell">Sync: </td>
|
||||
<td className="character-int-cell" style={style}>
|
||||
{numeralWrapper.formatSleeveSynchro(sleeve.sync)}
|
||||
{numeralWrapper.formatSleeveSynchro(props.sleeve.sync)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-int-cell">Memory: </td>
|
||||
<td className="character-int-cell" style={style}>
|
||||
{numeralWrapper.formatSleeveMemory(sleeve.memory)}
|
||||
{numeralWrapper.formatSleeveMemory(props.sleeve.memory)}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
import React, { useState } from "react";
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
import { SleeveTaskType } from "../SleeveTaskTypesEnum";
|
||||
import { Crimes } from "../../../Crime/Crimes";
|
||||
import { LocationName } from "../../../Locations/data/LocationNames";
|
||||
import { CityName } from "../../../Locations/data/CityNames";
|
||||
import { Factions } from "../../../Faction/Factions";
|
||||
import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum";
|
||||
|
||||
const universitySelectorOptions: string[] = [
|
||||
"Study Computer Science",
|
||||
"Data Structures",
|
||||
"Networks",
|
||||
"Algorithms",
|
||||
"Management",
|
||||
"Leadership",
|
||||
];
|
||||
|
||||
const gymSelectorOptions: string[] = ["Train Strength", "Train Defense", "Train Dexterity", "Train Agility"];
|
||||
|
||||
interface IProps {
|
||||
sleeve: Sleeve;
|
||||
player: IPlayer;
|
||||
setABC: (abc: string[]) => void;
|
||||
}
|
||||
|
||||
interface ITaskDetails {
|
||||
first: string[];
|
||||
second: (s1: string) => string[];
|
||||
}
|
||||
|
||||
function possibleJobs(player: IPlayer, sleeve: Sleeve): string[] {
|
||||
// Array of all companies that other sleeves are working at
|
||||
const forbiddenCompanies = [];
|
||||
for (const otherSleeve of player.sleeves) {
|
||||
if (sleeve === otherSleeve) {
|
||||
continue;
|
||||
}
|
||||
if (otherSleeve.currentTask === SleeveTaskType.Company) {
|
||||
forbiddenCompanies.push(otherSleeve.currentTaskLocation);
|
||||
}
|
||||
}
|
||||
let allJobs: string[] = Object.keys(player.jobs);
|
||||
for (let i = 0; i < allJobs.length; ++i) {
|
||||
if (!forbiddenCompanies.includes(allJobs[i])) {
|
||||
allJobs[i];
|
||||
}
|
||||
}
|
||||
|
||||
return allJobs;
|
||||
}
|
||||
|
||||
function possibleFactions(player: IPlayer, sleeve: Sleeve): string[] {
|
||||
// Array of all factions that other sleeves are working for
|
||||
const forbiddenFactions = [];
|
||||
for (const otherSleeve of player.sleeves) {
|
||||
if (sleeve === otherSleeve) {
|
||||
continue;
|
||||
}
|
||||
if (otherSleeve.currentTask === SleeveTaskType.Faction) {
|
||||
forbiddenFactions.push(otherSleeve.currentTaskLocation);
|
||||
}
|
||||
}
|
||||
|
||||
const factions = [];
|
||||
for (const fac of player.factions) {
|
||||
if (!forbiddenFactions.includes(fac)) {
|
||||
factions.push(fac);
|
||||
}
|
||||
}
|
||||
|
||||
return factions;
|
||||
}
|
||||
|
||||
const tasks: {
|
||||
[key: string]: undefined | ((player: IPlayer, sleeve: Sleeve) => ITaskDetails);
|
||||
["------"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Work for Company"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Work for Faction"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Commit Crime"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Take University Course"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Workout at Gym"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Shock Recovery"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Synchronize"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
} = {
|
||||
"------": (player: IPlayer, sleeve: Sleeve): ITaskDetails => {
|
||||
return { first: ["------"], second: () => ["------"] };
|
||||
},
|
||||
"Work for Company": (player: IPlayer, sleeve: Sleeve): ITaskDetails => {
|
||||
let jobs = possibleJobs(player, sleeve);
|
||||
|
||||
if (jobs.length === 0) jobs = ["------"];
|
||||
return { first: jobs, second: () => ["------"] };
|
||||
},
|
||||
"Work for Faction": (player: IPlayer, sleeve: Sleeve): ITaskDetails => {
|
||||
let factions = possibleFactions(player, sleeve);
|
||||
if (factions.length === 0) factions = ["------"];
|
||||
|
||||
return {
|
||||
first: factions,
|
||||
second: (s1: string) => {
|
||||
const faction = Factions[s1];
|
||||
const facInfo = faction.getInfo();
|
||||
const options: string[] = [];
|
||||
if (facInfo.offerHackingWork) {
|
||||
options.push("Hacking Contracts");
|
||||
}
|
||||
if (facInfo.offerFieldWork) {
|
||||
options.push("Field Work");
|
||||
}
|
||||
if (facInfo.offerSecurityWork) {
|
||||
options.push("Security Work");
|
||||
}
|
||||
return options;
|
||||
},
|
||||
};
|
||||
},
|
||||
"Commit Crime": (player: IPlayer, sleeve: Sleeve): ITaskDetails => {
|
||||
return { first: Object.keys(Crimes), second: () => ["------"] };
|
||||
},
|
||||
"Take University Course": (player: IPlayer, sleeve: Sleeve): ITaskDetails => {
|
||||
let universities: string[] = [];
|
||||
switch (sleeve.city) {
|
||||
case CityName.Aevum:
|
||||
universities = [LocationName.AevumSummitUniversity];
|
||||
break;
|
||||
case CityName.Sector12:
|
||||
universities = [LocationName.Sector12RothmanUniversity];
|
||||
break;
|
||||
case CityName.Volhaven:
|
||||
universities = [LocationName.VolhavenZBInstituteOfTechnology];
|
||||
break;
|
||||
default:
|
||||
universities = ["No university available in city!"];
|
||||
break;
|
||||
}
|
||||
|
||||
return { first: universitySelectorOptions, second: () => universities };
|
||||
},
|
||||
"Workout at Gym": (player: IPlayer, sleeve: Sleeve): ITaskDetails => {
|
||||
let gyms: string[] = [];
|
||||
switch (sleeve.city) {
|
||||
case CityName.Aevum:
|
||||
gyms = [LocationName.AevumCrushFitnessGym, LocationName.AevumSnapFitnessGym];
|
||||
break;
|
||||
case CityName.Sector12:
|
||||
gyms = [LocationName.Sector12IronGym, LocationName.Sector12PowerhouseGym];
|
||||
break;
|
||||
case CityName.Volhaven:
|
||||
gyms = [LocationName.VolhavenMilleniumFitnessGym];
|
||||
break;
|
||||
default:
|
||||
gyms = ["No gym available in city!"];
|
||||
break;
|
||||
}
|
||||
|
||||
return { first: gymSelectorOptions, second: () => gyms };
|
||||
},
|
||||
"Shock Recovery": (player: IPlayer, sleeve: Sleeve): ITaskDetails => {
|
||||
return { first: ["------"], second: () => ["------"] };
|
||||
},
|
||||
Synchronize: (player: IPlayer, sleeve: Sleeve): ITaskDetails => {
|
||||
return { first: ["------"], second: () => ["------"] };
|
||||
},
|
||||
};
|
||||
|
||||
const canDo: {
|
||||
[key: string]: undefined | ((player: IPlayer, sleeve: Sleeve) => boolean);
|
||||
["------"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Work for Company"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Work for Faction"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Commit Crime"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Take University Course"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Workout at Gym"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Shock Recovery"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Synchronize"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
} = {
|
||||
["------"]: () => true,
|
||||
["Work for Company"]: (player: IPlayer, sleeve: Sleeve) => possibleJobs(player, sleeve).length > 0,
|
||||
["Work for Faction"]: (player: IPlayer, sleeve: Sleeve) => possibleFactions(player, sleeve).length > 0,
|
||||
["Commit Crime"]: () => true,
|
||||
["Take University Course"]: (player: IPlayer, sleeve: Sleeve) =>
|
||||
[CityName.Aevum, CityName.Sector12, CityName.Volhaven].includes(sleeve.city),
|
||||
["Workout at Gym"]: (player: IPlayer, sleeve: Sleeve) =>
|
||||
[CityName.Aevum, CityName.Sector12, CityName.Volhaven].includes(sleeve.city),
|
||||
["Shock Recovery"]: (player: IPlayer, sleeve: Sleeve) => sleeve.shock < 100,
|
||||
["Synchronize"]: (player: IPlayer, sleeve: Sleeve) => sleeve.sync < 100,
|
||||
};
|
||||
|
||||
function getABC(sleeve: Sleeve): [string, string, string] {
|
||||
switch (sleeve.currentTask) {
|
||||
case SleeveTaskType.Idle:
|
||||
return ["------", "------", "------"];
|
||||
case SleeveTaskType.Company:
|
||||
return ["Work for Company", sleeve.currentTaskLocation, "------"];
|
||||
case SleeveTaskType.Faction:
|
||||
let workType = "";
|
||||
switch (sleeve.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", sleeve.currentTaskLocation, workType];
|
||||
case SleeveTaskType.Crime:
|
||||
return ["Commit Crime", sleeve.crimeType, "------"];
|
||||
case SleeveTaskType.Class:
|
||||
return ["Take University Course", sleeve.className, sleeve.currentTaskLocation];
|
||||
case SleeveTaskType.Gym:
|
||||
return ["Workout at Gym", sleeve.gymStatType, sleeve.currentTaskLocation];
|
||||
case SleeveTaskType.Recovery:
|
||||
return ["Shock Recovery", "------", "------"];
|
||||
case SleeveTaskType.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 validActions = Object.keys(canDo).filter((k) =>
|
||||
(canDo[k] as (player: IPlayer, sleeve: Sleeve) => boolean)(props.player, props.sleeve),
|
||||
);
|
||||
|
||||
const detailsF = tasks[s0];
|
||||
if (detailsF === undefined) throw new Error(`No function for task '${s0}'`);
|
||||
const details = detailsF(props.player, props.sleeve);
|
||||
const details2 = details.second(s1);
|
||||
|
||||
function onS0Change(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
const n = event.target.value;
|
||||
const detailsF = tasks[n];
|
||||
if (detailsF === undefined) throw new Error(`No function for task '${s0}'`);
|
||||
const details = detailsF(props.player, 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: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
setS1(event.target.value);
|
||||
props.setABC([s0, event.target.value, s2]);
|
||||
}
|
||||
|
||||
function onS2Change(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
setS2(event.target.value);
|
||||
props.setABC([s0, s1, event.target.value]);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<select className="dropdown" onChange={onS0Change} defaultValue={s0}>
|
||||
{validActions.map((task) => (
|
||||
<option key={task} value={task}>
|
||||
{task}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{!(details.first.length === 1 && details.first[0] === "------") && (
|
||||
<select className="dropdown" onChange={onS1Change} defaultValue={s1}>
|
||||
{details.first.map((detail) => (
|
||||
<option key={detail} value={detail}>
|
||||
{detail}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
{!(details2.length === 1 && details2[0] === "------") && (
|
||||
<select className="dropdown" onChange={onS2Change} defaultValue={s2}>
|
||||
{details2.map((detail) => (
|
||||
<option key={detail} value={detail}>
|
||||
{detail}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -12,6 +12,7 @@ interface IProps {
|
||||
popupId: string;
|
||||
sleeve: Sleeve;
|
||||
player: IPlayer;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
export function TravelPopup(props: IProps): React.ReactElement {
|
||||
@@ -23,6 +24,7 @@ export function TravelPopup(props: IProps): React.ReactElement {
|
||||
props.player.loseMoney(CONSTANTS.TravelCost);
|
||||
props.sleeve.resetTaskStatus();
|
||||
removePopup(props.popupId);
|
||||
props.rerender();
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -35,7 +37,7 @@ export function TravelPopup(props: IProps): React.ReactElement {
|
||||
{Object.keys(Cities)
|
||||
.filter((city: string) => props.sleeve.city !== city)
|
||||
.map((city: string) => (
|
||||
<div className="cmpy-mgmt-find-employee-option" onClick={() => travel(city)}>
|
||||
<div key={city} className="cmpy-mgmt-find-employee-option" onClick={() => travel(city)}>
|
||||
{city}
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user