mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-10 01:33:54 +01:00
sleeves to mui
This commit is contained in:
parent
3289f76cd0
commit
14e6dd0158
@ -14,7 +14,6 @@ import { CONSTANTS } from "../../Constants";
|
||||
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { createSleevePurchasesFromCovenantPopup } from "../../PersonObjects/Sleeve/SleeveCovenantPurchases";
|
||||
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
||||
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
@ -23,6 +22,7 @@ import { CreateGangPopup } from "./CreateGangPopup";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot";
|
||||
|
||||
type IProps = {
|
||||
faction: Faction;
|
||||
@ -66,6 +66,7 @@ const GangNames = [
|
||||
];
|
||||
|
||||
export function FactionRoot(props: IProps): React.ReactElement {
|
||||
const [sleevesOpen, setSleevesOpen] = useState(false);
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
@ -101,10 +102,6 @@ export function FactionRoot(props: IProps): React.ReactElement {
|
||||
setPurchasingAugs(true);
|
||||
}
|
||||
|
||||
function sleevePurchases(): void {
|
||||
createSleevePurchasesFromCovenantPopup(player);
|
||||
}
|
||||
|
||||
function startFieldWork(faction: Faction): void {
|
||||
player.startFactionFieldWork(router, faction);
|
||||
}
|
||||
@ -185,11 +182,14 @@ export function FactionRoot(props: IProps): React.ReactElement {
|
||||
)}
|
||||
<Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={routeToPurchaseAugs} />
|
||||
{canPurchaseSleeves && (
|
||||
<Option
|
||||
buttonText={"Purchase & Upgrade Duplicate Sleeves"}
|
||||
infoText={sleevePurchasesInfo}
|
||||
onClick={sleevePurchases}
|
||||
/>
|
||||
<>
|
||||
<Option
|
||||
buttonText={"Purchase & Upgrade Duplicate Sleeves"}
|
||||
infoText={sleevePurchasesInfo}
|
||||
onClick={() => setSleevesOpen(true)}
|
||||
/>
|
||||
<CovenantPurchasesRoot open={sleevesOpen} onClose={() => setSleevesOpen(false)} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@ -537,6 +537,7 @@ export class Sleeve extends Person {
|
||||
break;
|
||||
case SleeveTaskType.Synchro:
|
||||
this.sync = Math.min(100, this.sync + p.getIntelligenceBonus(0.5) * 0.0002 * cyclesUsed);
|
||||
if (this.sync >= 100) this.resetTaskStatus();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Implements the purchasing of extra Duplicate Sleeves from The Covenant,
|
||||
* as well as the purchasing of upgrades (memory)
|
||||
*/
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
import { CovenantPurchasesRoot } from "./ui/CovenantPurchasesRoot";
|
||||
import { createPopup, removePopup } from "../../ui/React/createPopup";
|
||||
|
||||
export const MaxSleevesFromCovenant = 5;
|
||||
export const BaseCostPerSleeve = 10e12;
|
||||
export const PopupId = "covenant-sleeve-purchases-popup";
|
||||
|
||||
export function createSleevePurchasesFromCovenantPopup(p: IPlayer): void {
|
||||
createPopup(PopupId, CovenantPurchasesRoot, {
|
||||
p: p,
|
||||
closeFn: () => removePopup(PopupId),
|
||||
});
|
||||
}
|
7
src/PersonObjects/Sleeve/SleeveCovenantPurchases.tsx
Normal file
7
src/PersonObjects/Sleeve/SleeveCovenantPurchases.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Implements the purchasing of extra Duplicate Sleeves from The Covenant,
|
||||
* as well as the purchasing of upgrades (memory)
|
||||
*/
|
||||
|
||||
export const MaxSleevesFromCovenant = 5;
|
||||
export const BaseCostPerSleeve = 10e12;
|
@ -4,31 +4,33 @@
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { CovenantSleeveUpgrades } from "./CovenantSleeveUpgrades";
|
||||
import { CovenantSleeveMemoryUpgrade } from "./CovenantSleeveMemoryUpgrade";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { BaseCostPerSleeve, MaxSleevesFromCovenant, PopupId } from "../SleeveCovenantPurchases";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
import { BaseCostPerSleeve, MaxSleevesFromCovenant } from "../SleeveCovenantPurchases";
|
||||
|
||||
import { PopupCloseButton } from "../../../ui/React/PopupCloseButton";
|
||||
import { StdButton } from "../../../ui/React/StdButton";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import { Modal } from "../../../ui/React/Modal";
|
||||
import { use } from "../../../ui/Context";
|
||||
|
||||
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
interface IProps {
|
||||
closeFn: () => void;
|
||||
p: IPlayer;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function CovenantPurchasesRoot(props: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const [update, setUpdate] = useState(0);
|
||||
|
||||
/**
|
||||
* Get the cost to purchase a new Duplicate Sleeve
|
||||
*/
|
||||
function purchaseCost(): number {
|
||||
return (props.p.sleevesFromCovenant + 1) * BaseCostPerSleeve;
|
||||
return (player.sleevesFromCovenant + 1) * BaseCostPerSleeve;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,20 +42,20 @@ export function CovenantPurchasesRoot(props: IProps): React.ReactElement {
|
||||
|
||||
// Purchasing a new Duplicate Sleeve
|
||||
let purchaseDisabled = false;
|
||||
if (!props.p.canAfford(purchaseCost())) {
|
||||
if (!player.canAfford(purchaseCost())) {
|
||||
purchaseDisabled = true;
|
||||
}
|
||||
if (props.p.sleevesFromCovenant >= MaxSleevesFromCovenant) {
|
||||
if (player.sleevesFromCovenant >= MaxSleevesFromCovenant) {
|
||||
purchaseDisabled = true;
|
||||
}
|
||||
|
||||
function purchaseOnClick(): void {
|
||||
if (props.p.sleevesFromCovenant >= MaxSleevesFromCovenant) return;
|
||||
if (player.sleevesFromCovenant >= MaxSleevesFromCovenant) return;
|
||||
|
||||
if (props.p.canAfford(purchaseCost())) {
|
||||
props.p.loseMoney(purchaseCost());
|
||||
props.p.sleevesFromCovenant += 1;
|
||||
props.p.sleeves.push(new Sleeve(props.p));
|
||||
if (player.canAfford(purchaseCost())) {
|
||||
player.loseMoney(purchaseCost());
|
||||
player.sleevesFromCovenant += 1;
|
||||
player.sleeves.push(new Sleeve(player));
|
||||
rerender();
|
||||
} else {
|
||||
dialogBoxCreate(`You cannot afford to purchase a Duplicate Sleeve`, false);
|
||||
@ -62,35 +64,31 @@ export function CovenantPurchasesRoot(props: IProps): React.ReactElement {
|
||||
|
||||
// Purchasing Upgrades for Sleeves
|
||||
const upgradePanels = [];
|
||||
for (let i = 0; i < props.p.sleeves.length; ++i) {
|
||||
const sleeve = props.p.sleeves[i];
|
||||
upgradePanels.push(<CovenantSleeveUpgrades {...props} sleeve={sleeve} index={i} rerender={rerender} key={i} />);
|
||||
for (let i = 0; i < player.sleeves.length; ++i) {
|
||||
const sleeve = player.sleeves[i];
|
||||
upgradePanels.push(<CovenantSleeveMemoryUpgrade index={i} p={player} rerender={rerender} sleeve={sleeve} />);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PopupCloseButton popup={PopupId} text={"Close"} />
|
||||
{props.p.sleevesFromCovenant < MaxSleevesFromCovenant && (
|
||||
<>
|
||||
<p>
|
||||
Would you like to purchase an additional Duplicate Sleeve from The Covenant for{" "}
|
||||
<Money money={purchaseCost()} player={props.p} />?
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
These Duplicate Sleeves are permanent (they persist through BitNodes). You can purchase a total of{" "}
|
||||
{MaxSleevesFromCovenant} from The Covenant.
|
||||
</p>
|
||||
<StdButton disabled={purchaseDisabled} onClick={purchaseOnClick} text={"Purchase"} />
|
||||
</>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<p>
|
||||
Here, you can also purchase upgrades for your Duplicate Sleeves. These upgrades are also permanent, meaning they
|
||||
persist across BitNodes.
|
||||
</p>
|
||||
{upgradePanels}
|
||||
</div>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<>
|
||||
{player.sleevesFromCovenant < MaxSleevesFromCovenant && (
|
||||
<>
|
||||
<Typography>
|
||||
Purchase an additional Sleeves. These Duplicate Sleeves are permanent (they persist through BitNodes). You
|
||||
can purchase a total of {MaxSleevesFromCovenant} from The Covenant.
|
||||
</Typography>
|
||||
<Button disabled={purchaseDisabled} onClick={purchaseOnClick}>
|
||||
Purchase -
|
||||
<Money money={purchaseCost()} player={player} />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<Typography>You can also purchase upgrades for your Sleeves. These upgrades are also permanent.</Typography>
|
||||
{upgradePanels}
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
@ -2,15 +2,20 @@
|
||||
* React component for a panel that lets you purchase upgrades for a Duplicate
|
||||
* Sleeve's Memory (through The Covenant)
|
||||
*/
|
||||
import * as React from "react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import { StdButton } from "../../../ui/React/StdButton";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Button from "@mui/material/Button";
|
||||
import Box from "@mui/material/Box";
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
interface IProps {
|
||||
index: number;
|
||||
p: IPlayer;
|
||||
@ -18,99 +23,74 @@ interface IProps {
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
amt: number;
|
||||
}
|
||||
export function CovenantSleeveMemoryUpgrade(props: IProps): React.ReactElement {
|
||||
const [amt, setAmt] = useState(1);
|
||||
|
||||
export class CovenantSleeveMemoryUpgrade extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
amt: 1,
|
||||
};
|
||||
|
||||
this.changePurchaseAmount = this.changePurchaseAmount.bind(this);
|
||||
this.purchaseMemory = this.purchaseMemory.bind(this);
|
||||
}
|
||||
|
||||
changePurchaseAmount(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||
function changePurchaseAmount(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||
let n: number = parseInt(e.target.value);
|
||||
|
||||
if (isNaN(n)) n = 1;
|
||||
const maxMemory = 100 - this.props.sleeve.memory;
|
||||
const maxMemory = 100 - props.sleeve.memory;
|
||||
if (n > maxMemory) n = maxMemory;
|
||||
|
||||
this.setState({
|
||||
amt: n,
|
||||
});
|
||||
setAmt(n);
|
||||
}
|
||||
|
||||
getPurchaseCost(): number {
|
||||
if (isNaN(this.state.amt)) {
|
||||
function getPurchaseCost(): number {
|
||||
if (isNaN(amt)) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const maxMemory = 100 - this.props.sleeve.memory;
|
||||
if (this.state.amt > maxMemory) {
|
||||
const maxMemory = 100 - props.sleeve.memory;
|
||||
if (amt > maxMemory) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
return this.props.sleeve.getMemoryUpgradeCost(this.state.amt);
|
||||
return props.sleeve.getMemoryUpgradeCost(amt);
|
||||
}
|
||||
|
||||
purchaseMemory(): void {
|
||||
const cost = this.getPurchaseCost();
|
||||
if (this.props.p.canAfford(cost)) {
|
||||
this.props.sleeve.upgradeMemory(this.state.amt);
|
||||
this.props.p.loseMoney(cost);
|
||||
this.props.rerender();
|
||||
function purchaseMemory(): void {
|
||||
const cost = getPurchaseCost();
|
||||
if (props.p.canAfford(cost)) {
|
||||
props.sleeve.upgradeMemory(amt);
|
||||
props.p.loseMoney(cost);
|
||||
props.rerender();
|
||||
}
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const inputId = `sleeve-${this.props.index}-memory-upgrade-input`;
|
||||
|
||||
// Memory cannot go above 100
|
||||
const maxMemory = 100 - this.props.sleeve.memory;
|
||||
|
||||
// Purchase button props
|
||||
const cost = this.getPurchaseCost();
|
||||
const purchaseBtnDisabled = !this.props.p.canAfford(cost);
|
||||
let purchaseBtnContent;
|
||||
if (isNaN(this.state.amt)) {
|
||||
purchaseBtnContent = <>Invalid value</>;
|
||||
} else if (this.state.amt > maxMemory) {
|
||||
purchaseBtnContent = <>Memory cannot exceed 100</>;
|
||||
} else {
|
||||
purchaseBtnContent = (
|
||||
<>
|
||||
Purchase {this.state.amt} memory - <Money money={cost} player={this.props.p} />?
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>
|
||||
<u>Upgrade Memory</u>
|
||||
</h2>
|
||||
<p>
|
||||
Purchase a memory upgrade for your sleeve. Note that a sleeve's max memory is 100 (current:{" "}
|
||||
{numeralWrapper.formatSleeveMemory(this.props.sleeve.memory)})
|
||||
</p>
|
||||
|
||||
<label htmlFor={inputId}>Amount of memory to purchase (must be an integer):</label>
|
||||
<input
|
||||
className="text-input"
|
||||
id={inputId}
|
||||
onChange={this.changePurchaseAmount}
|
||||
type={"number"}
|
||||
value={this.state.amt}
|
||||
/>
|
||||
<br />
|
||||
<StdButton disabled={purchaseBtnDisabled} onClick={this.purchaseMemory} text={purchaseBtnContent} />
|
||||
</div>
|
||||
// Purchase button props
|
||||
const cost = getPurchaseCost();
|
||||
const purchaseBtnDisabled = !props.p.canAfford(cost);
|
||||
let purchaseBtnContent = <></>;
|
||||
if (isNaN(amt)) {
|
||||
purchaseBtnContent = <>Invalid value</>;
|
||||
} else {
|
||||
purchaseBtnContent = (
|
||||
<>
|
||||
Purchase {amt} memory -
|
||||
<Money money={cost} player={props.p} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
<Typography variant="h6" color="primary">
|
||||
Upgrade Memory of Sleeve {props.index}
|
||||
</Typography>
|
||||
<Typography>
|
||||
Purchase a memory upgrade for your sleeve. Note that a sleeve's max memory is 100 (current:{" "}
|
||||
{numeralWrapper.formatSleeveMemory(props.sleeve.memory)})
|
||||
</Typography>
|
||||
|
||||
<Box display="flex" flexDirection="row" alignItems="center">
|
||||
<Typography>Amount of memory to purchase (must be an integer): </Typography>
|
||||
<TextField variant="standard" onChange={changePurchaseAmount} type={"number"} value={amt} />
|
||||
</Box>
|
||||
<br />
|
||||
<Button disabled={purchaseBtnDisabled} onClick={purchaseMemory}>
|
||||
{purchaseBtnContent}
|
||||
</Button>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* React Component for a panel that lets you purchase upgrades for a single
|
||||
* Duplicate Sleeve through The Covenant
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { CovenantSleeveMemoryUpgrade } from "./CovenantSleeveMemoryUpgrade";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
|
||||
interface IProps {
|
||||
index: number;
|
||||
p: IPlayer;
|
||||
rerender: () => void;
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
export class CovenantSleeveUpgrades extends React.Component<IProps, any> {
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<div className={"bladeburner-action"}>
|
||||
<h1>Duplicate Sleeve {this.props.index}</h1>
|
||||
<CovenantSleeveMemoryUpgrade {...this.props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import * as React from "react";
|
||||
|
||||
interface IProps {
|
||||
title: string;
|
||||
stats: any[][];
|
||||
}
|
||||
|
||||
export function EarningsTableElement(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<pre>{props.title}</pre>
|
||||
<table>
|
||||
<tbody>
|
||||
{props.stats.map((stat: any[], i: number) => (
|
||||
<tr key={i}>
|
||||
{stat.map((s: any, i: number) => {
|
||||
let style = {};
|
||||
if (i !== 0) {
|
||||
style = { textAlign: "right" };
|
||||
}
|
||||
return (
|
||||
<td style={style} key={i}>
|
||||
{s}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
118
src/PersonObjects/Sleeve/ui/FAQModal.tsx
Normal file
118
src/PersonObjects/Sleeve/ui/FAQModal.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
import React from "react";
|
||||
|
||||
import { Modal } from "../../../ui/React/Modal";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function FAQModal({ open, onClose }: IProps): React.ReactElement {
|
||||
return (
|
||||
<Modal open={open} onClose={onClose}>
|
||||
<>
|
||||
<Typography variant="h4">How do Duplicate Sleeves work?</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Duplicate Sleeves are essentially clones. You can use them to perform any work type action, such as working
|
||||
for a company/faction or committing a crime. Having sleeves perform these tasks earns you money, experience,
|
||||
and reputation.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography>
|
||||
Sleeves are their own individuals, which means they each have their own experience and stats.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography>
|
||||
When a sleeve earns experience, it earns experience for itself, the player's original 'consciousness', as well
|
||||
as all of the player's other sleeves.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography variant="h4">What is Synchronization (Sync)?</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Synchronization is a measure of how aligned your consciousness is with that of your Duplicate Sleeves. It is a
|
||||
numerical value between 1 and 100, and it affects how much experience is earned when the sleeve is performing
|
||||
a task.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography>
|
||||
Let N be the sleeve's synchronization. When the sleeve earns experience by performing a task, both the sleeve
|
||||
and the player's original host consciousness earn N% of the amount of experience normally earned by the task.
|
||||
All of the player's other sleeves earn ((N/100)^2 * 100)% of the experience.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography>Synchronization can be increased by assigning sleeves to the 'Synchronize' task.</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography variant="h4">What is Shock?</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Sleeve shock is a measure of how much trauma the sleeve has due to being placed in a new body. It is a
|
||||
numerical value between 0 and 99, where 99 indicates full shock and 0 indicates no shock. Shock affects the
|
||||
amount of experience earned by the sleeve.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography>
|
||||
Sleeve shock slowly decreases over time. You can further increase the rate at which it decreases by assigning
|
||||
sleeves to the 'Shock Recovery' task.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography variant="h4">Why can't I work for this company or faction?</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Only one of your sleeves can work for a given company/faction a time. To clarify further, if you have two
|
||||
sleeves they can work for two different companies, but they cannot both work for the same company.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography variant="h4">Why did my Sleeve stop working?</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Sleeves are subject to the same time restrictions as you. This means that they automatically stop working at a
|
||||
company after 8 hours, and stop working for a faction after 20 hours.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography variant="h4">How do I buy Augmentations for my Sleeves?</Typography>
|
||||
<br />
|
||||
<Typography>Your Sleeve needs to have a Shock of 0 in order for you to buy Augmentations for it.</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography variant="h4">Why can't I buy the X Augmentation for my sleeve?</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Certain Augmentations, like Bladeburner-specific ones and NeuroFlux Governor, are not available for sleeves.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography variant="h4">Do sleeves get reset when installing Augmentations or switching BitNodes?</Typography>
|
||||
<br />
|
||||
<Typography>Sleeves are reset when switching BitNodes, but not when installing Augmentations.</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography variant="h4">What is Memory?</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Sleeve memory dictates what a sleeve's synchronization will be when its reset by switching BitNodes. For
|
||||
example, if a sleeve has a memory of 25, then when you switch BitNodes its synchronization will initially be
|
||||
set to 25, rather than 1.
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography>
|
||||
Memory can only be increased by purchasing upgrades from The Covenant. It is a persistent stat, meaning it
|
||||
never gets resets back to 1. The maximum possible value for a sleeve's memory is 100.
|
||||
</Typography>
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -3,14 +3,17 @@ import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import * as React from "react";
|
||||
import { StatsTable } from "../../../ui/React/StatsTable";
|
||||
import { Modal } from "../../../ui/React/Modal";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
export function MoreEarningsContent(props: IProps): React.ReactElement {
|
||||
export function MoreEarningsModal(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<StatsTable
|
||||
rows={[
|
||||
["Money ", <Money money={props.sleeve.earningsForTask.money} />],
|
||||
@ -50,6 +53,6 @@ export function MoreEarningsContent(props: IProps): React.ReactElement {
|
||||
title="Total Earnings for Other Sleeves:"
|
||||
/>
|
||||
<br />
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import { StatsTable } from "../../../ui/React/StatsTable";
|
||||
import * as React from "react";
|
||||
|
||||
interface IProps {
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
export function MoreStatsContent(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<StatsTable
|
||||
rows={[
|
||||
["Hacking: ", props.sleeve.hacking_skill, `(${numeralWrapper.formatExp(props.sleeve.hacking_exp)} exp)`],
|
||||
["Strength: ", props.sleeve.strength, `(${numeralWrapper.formatExp(props.sleeve.strength_exp)} exp)`],
|
||||
["Defense: ", props.sleeve.defense, `(${numeralWrapper.formatExp(props.sleeve.defense_exp)} exp)`],
|
||||
["Dexterity: ", props.sleeve.dexterity, `(${numeralWrapper.formatExp(props.sleeve.dexterity_exp)} exp)`],
|
||||
["Agility: ", props.sleeve.agility, `(${numeralWrapper.formatExp(props.sleeve.agility_exp)} exp)`],
|
||||
["Charisma: ", props.sleeve.charisma, `(${numeralWrapper.formatExp(props.sleeve.charisma_exp)} exp)`],
|
||||
]}
|
||||
title="Stats:"
|
||||
/>
|
||||
<br />
|
||||
<StatsTable
|
||||
rows={[
|
||||
["Hacking Level multiplier: ", numeralWrapper.formatPercentage(props.sleeve.hacking_mult)],
|
||||
["Hacking Experience multiplier: ", numeralWrapper.formatPercentage(props.sleeve.hacking_exp_mult)],
|
||||
["Strength Level multiplier: ", numeralWrapper.formatPercentage(props.sleeve.strength_mult)],
|
||||
["Strength Experience multiplier: ", numeralWrapper.formatPercentage(props.sleeve.strength_exp_mult)],
|
||||
["Defense Level multiplier: ", numeralWrapper.formatPercentage(props.sleeve.defense_mult)],
|
||||
["Defense Experience multiplier: ", numeralWrapper.formatPercentage(props.sleeve.defense_exp_mult)],
|
||||
["Dexterity Level multiplier: ", numeralWrapper.formatPercentage(props.sleeve.dexterity_mult)],
|
||||
["Dexterity Experience multiplier: ", numeralWrapper.formatPercentage(props.sleeve.dexterity_exp_mult)],
|
||||
["Agility Level multiplier: ", numeralWrapper.formatPercentage(props.sleeve.agility_mult)],
|
||||
["Agility Experience multiplier: ", numeralWrapper.formatPercentage(props.sleeve.agility_exp_mult)],
|
||||
["Charisma Level multiplier: ", numeralWrapper.formatPercentage(props.sleeve.charisma_mult)],
|
||||
["Charisma Experience multiplier: ", numeralWrapper.formatPercentage(props.sleeve.charisma_exp_mult)],
|
||||
["Faction Reputation Gain multiplier: ", numeralWrapper.formatPercentage(props.sleeve.faction_rep_mult)],
|
||||
["Company Reputation Gain multiplier: ", numeralWrapper.formatPercentage(props.sleeve.company_rep_mult)],
|
||||
["Salary multiplier: ", numeralWrapper.formatPercentage(props.sleeve.work_money_mult)],
|
||||
["Crime Money multiplier: ", numeralWrapper.formatPercentage(props.sleeve.crime_money_mult)],
|
||||
["Crime Success multiplier: ", numeralWrapper.formatPercentage(props.sleeve.crime_success_mult)],
|
||||
]}
|
||||
title="Multipliers:"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
85
src/PersonObjects/Sleeve/ui/MoreStatsModal.tsx
Normal file
85
src/PersonObjects/Sleeve/ui/MoreStatsModal.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import { StatsTable } from "../../../ui/React/StatsTable";
|
||||
import { Modal } from "../../../ui/React/Modal";
|
||||
import React from "react";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
export function MoreStatsModal(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<StatsTable
|
||||
rows={[
|
||||
[
|
||||
<>Hacking: </>,
|
||||
props.sleeve.hacking_skill,
|
||||
<> ({numeralWrapper.formatExp(props.sleeve.hacking_exp)} exp)</>,
|
||||
],
|
||||
[
|
||||
<>Strength: </>,
|
||||
props.sleeve.strength,
|
||||
<> ({numeralWrapper.formatExp(props.sleeve.strength_exp)} exp)</>,
|
||||
],
|
||||
[
|
||||
<>Defense: </>,
|
||||
props.sleeve.defense,
|
||||
<> ({numeralWrapper.formatExp(props.sleeve.defense_exp)} exp)</>,
|
||||
],
|
||||
[
|
||||
<>Dexterity: </>,
|
||||
props.sleeve.dexterity,
|
||||
<> ({numeralWrapper.formatExp(props.sleeve.dexterity_exp)} exp)</>,
|
||||
],
|
||||
[
|
||||
<>Agility: </>,
|
||||
props.sleeve.agility,
|
||||
<> ({numeralWrapper.formatExp(props.sleeve.agility_exp)} exp)</>,
|
||||
],
|
||||
[
|
||||
<>Charisma: </>,
|
||||
props.sleeve.charisma,
|
||||
<> ({numeralWrapper.formatExp(props.sleeve.charisma_exp)} exp)</>,
|
||||
],
|
||||
]}
|
||||
title="Stats:"
|
||||
/>
|
||||
<br />
|
||||
<StatsTable
|
||||
rows={[
|
||||
[<>Hacking Level multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.hacking_mult)],
|
||||
[<>Hacking Experience multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.hacking_exp_mult)],
|
||||
[<>Strength Level multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.strength_mult)],
|
||||
[<>Strength Experience multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.strength_exp_mult)],
|
||||
[<>Defense Level multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.defense_mult)],
|
||||
[<>Defense Experience multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.defense_exp_mult)],
|
||||
[<>Dexterity Level multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.dexterity_mult)],
|
||||
[
|
||||
<>Dexterity Experience multiplier: </>,
|
||||
numeralWrapper.formatPercentage(props.sleeve.dexterity_exp_mult),
|
||||
],
|
||||
[<>Agility Level multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.agility_mult)],
|
||||
[<>Agility Experience multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.agility_exp_mult)],
|
||||
[<>Charisma Level multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.charisma_mult)],
|
||||
[<>Charisma Experience multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.charisma_exp_mult)],
|
||||
[
|
||||
<>Faction Reputation Gain multiplier: </>,
|
||||
numeralWrapper.formatPercentage(props.sleeve.faction_rep_mult),
|
||||
],
|
||||
[
|
||||
<>Company Reputation Gain multiplier: </>,
|
||||
numeralWrapper.formatPercentage(props.sleeve.company_rep_mult),
|
||||
],
|
||||
[<>Salary multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.work_money_mult)],
|
||||
[<>Crime Money multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.crime_money_mult)],
|
||||
[<>Crime Success multiplier: </>, numeralWrapper.formatPercentage(props.sleeve.crime_success_mult)],
|
||||
]}
|
||||
title="Multipliers:"
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
126
src/PersonObjects/Sleeve/ui/SleeveAugmentationsModal.tsx
Normal file
126
src/PersonObjects/Sleeve/ui/SleeveAugmentationsModal.tsx
Normal file
@ -0,0 +1,126 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { findSleevePurchasableAugs } from "../SleeveHelpers";
|
||||
import { Augmentations } from "../../../Augmentation/Augmentations";
|
||||
import { Augmentation } from "../../../Augmentation/Augmentation";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import { Modal } from "../../../ui/React/Modal";
|
||||
import { use } from "../../../ui/Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import Table from "@mui/material/Table";
|
||||
import { TableCell } from "../../../ui/React/Table";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
export function SleeveAugmentationsModal(props: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(rerender, 150);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
// Array of all owned Augmentations. Names only
|
||||
const ownedAugNames = props.sleeve.augmentations.map((e) => e.name);
|
||||
|
||||
// You can only purchase Augmentations that are actually available from
|
||||
// your factions. I.e. you must be in a faction that has the Augmentation
|
||||
// and you must also have enough rep in that faction in order to purchase it.
|
||||
const availableAugs = findSleevePurchasableAugs(props.sleeve, player);
|
||||
|
||||
function purchaseAugmentation(aug: Augmentation): void {
|
||||
props.sleeve.tryBuyAugmentation(player, aug);
|
||||
rerender();
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<>
|
||||
<Typography>
|
||||
You can purchase Augmentations for your Duplicate Sleeves. These Augmentations have the same effect as they
|
||||
would for you. You can only purchase Augmentations that you have unlocked through Factions.
|
||||
<br />
|
||||
<br />
|
||||
When purchasing an Augmentation for a Duplicate Sleeve, they are immediately installed. This means that the
|
||||
Duplicate Sleeve will immediately lose all of its stat experience.
|
||||
</Typography>
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>
|
||||
{availableAugs.map((aug) => {
|
||||
return (
|
||||
<TableRow key={aug.name}>
|
||||
<TableCell>
|
||||
<Button onClick={() => purchaseAugmentation(aug)} disabled={player.money.lt(aug.startingCost)}>
|
||||
Buy
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box display="flex">
|
||||
<Tooltip disableInteractive title={aug.stats || ""}>
|
||||
<Typography>{aug.name}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Money money={aug.startingCost} player={player} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
{ownedAugNames.length > 0 && (
|
||||
<>
|
||||
<Typography>Owned Augmentations:</Typography>
|
||||
{ownedAugNames.map((augName) => {
|
||||
const aug = Augmentations[augName];
|
||||
let tooltip = <></>;
|
||||
if (typeof aug.info === "string") {
|
||||
tooltip = (
|
||||
<>
|
||||
<span>{aug.info}</span>
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
tooltip = (
|
||||
<>
|
||||
{aug.info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip key={augName} disableInteractive title={<Typography>{tooltip}</Typography>}>
|
||||
<Paper>
|
||||
<Typography>{augName}</Typography>
|
||||
</Paper>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { findSleevePurchasableAugs } from "../SleeveHelpers";
|
||||
import { Augmentations } from "../../../Augmentation/Augmentations";
|
||||
import { Augmentation } from "../../../Augmentation/Augmentation";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
|
||||
interface IProps {
|
||||
sleeve: Sleeve;
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
export function SleeveAugmentationsPopup(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(rerender, 150);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
// Array of all owned Augmentations. Names only
|
||||
const ownedAugNames = props.sleeve.augmentations.map((e) => e.name);
|
||||
|
||||
// You can only purchase Augmentations that are actually available from
|
||||
// your factions. I.e. you must be in a faction that has the Augmentation
|
||||
// and you must also have enough rep in that faction in order to purchase it.
|
||||
const availableAugs = findSleevePurchasableAugs(props.sleeve, props.player);
|
||||
|
||||
function purchaseAugmentation(aug: Augmentation): void {
|
||||
props.sleeve.tryBuyAugmentation(props.player, aug);
|
||||
rerender();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="noselect">
|
||||
<p style={{ display: "block" }}>Owned Augmentations:</p>
|
||||
<div style={{ width: "70%" }}>
|
||||
{ownedAugNames.map((augName) => {
|
||||
const aug = Augmentations[augName];
|
||||
let tooltip = aug.info;
|
||||
if (typeof tooltip !== "string") {
|
||||
tooltip = renderToStaticMarkup(tooltip);
|
||||
}
|
||||
tooltip += "<br /><br />";
|
||||
tooltip += renderToStaticMarkup(aug.stats || <></>);
|
||||
return (
|
||||
<div key={augName} className="gang-owned-upgrade tooltip">
|
||||
{augName}
|
||||
<span className="tooltiptext" dangerouslySetInnerHTML={{ __html: tooltip }}></span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p>
|
||||
You can purchase Augmentations for your Duplicate Sleeves. These Augmentations have the same effect as they
|
||||
would for you. You can only purchase Augmentations that you have unlocked through Factions.
|
||||
<br />
|
||||
<br />
|
||||
When purchasing an Augmentation for a Duplicate Sleeve, they are immediately installed. This means that the
|
||||
Duplicate Sleeve will immediately lose all of its stat experience.
|
||||
</p>
|
||||
{availableAugs.map((aug) => {
|
||||
let info = aug.info;
|
||||
if (typeof info !== "string") {
|
||||
info = renderToStaticMarkup(info);
|
||||
}
|
||||
info += "<br /><br />";
|
||||
info += renderToStaticMarkup(aug.stats || <></>);
|
||||
|
||||
return (
|
||||
<div key={aug.name} className="cmpy-mgmt-upgrade-div" onClick={() => purchaseAugmentation(aug)}>
|
||||
<div style={{ fontSize: "12px", padding: "2px" }}>
|
||||
<h2>{aug.name}</h2>
|
||||
<br />
|
||||
Cost: <Money money={aug.startingCost} player={props.player} />
|
||||
<br />
|
||||
<br />
|
||||
<span dangerouslySetInnerHTML={{ __html: info }}></span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -3,91 +3,72 @@ import React, { useState } from "react";
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { SleeveTaskType } from "../SleeveTaskTypesEnum";
|
||||
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
import { CONSTANTS } from "../../../Constants";
|
||||
|
||||
import { Crimes } from "../../../Crime/Crimes";
|
||||
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
|
||||
|
||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
|
||||
import { createPopup } from "../../../ui/React/createPopup";
|
||||
|
||||
import { SleeveAugmentationsPopup } from "../ui/SleeveAugmentationsPopup";
|
||||
import { TravelPopup } from "../ui/TravelPopup";
|
||||
import { EarningsTableElement } from "../ui/EarningsTableElement";
|
||||
import { SleeveAugmentationsModal } from "./SleeveAugmentationsModal";
|
||||
import { TravelModal } from "./TravelModal";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import { MoneyRate } from "../../../ui/React/MoneyRate";
|
||||
import { use } from "../../../ui/Context";
|
||||
import { ReputationRate } from "../../../ui/React/ReputationRate";
|
||||
import { StatsElement } from "../ui/StatsElement";
|
||||
import { MoreStatsContent } from "../ui/MoreStatsContent";
|
||||
import { MoreEarningsContent } from "../ui/MoreEarningsContent";
|
||||
import { MoreStatsModal } from "./MoreStatsModal";
|
||||
import { MoreEarningsModal } from "../ui/MoreEarningsModal";
|
||||
import { TaskSelector } from "../ui/TaskSelector";
|
||||
import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum";
|
||||
import { StatsTable } from "../../../ui/React/StatsTable";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Button from "@mui/material/Button";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
sleeve: Sleeve;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
export function SleeveElem(props: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const [statsOpen, setStatsOpen] = useState(false);
|
||||
const [earningsOpen, setEarningsOpen] = useState(false);
|
||||
const [travelOpen, setTravelOpen] = useState(false);
|
||||
const [augmentationsOpen, setAugmentationsOpen] = useState(false);
|
||||
|
||||
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
|
||||
switch (abc[0]) {
|
||||
case "------":
|
||||
break;
|
||||
case "Work for Company":
|
||||
props.sleeve.workForCompany(props.player, abc[1]);
|
||||
props.sleeve.workForCompany(player, abc[1]);
|
||||
break;
|
||||
case "Work for Faction":
|
||||
props.sleeve.workForFaction(props.player, abc[1], abc[2]);
|
||||
props.sleeve.workForFaction(player, abc[1], abc[2]);
|
||||
break;
|
||||
case "Commit Crime":
|
||||
props.sleeve.commitCrime(props.player, abc[1]);
|
||||
props.sleeve.commitCrime(player, abc[1]);
|
||||
break;
|
||||
case "Take University Course":
|
||||
props.sleeve.takeUniversityCourse(props.player, abc[2], abc[1]);
|
||||
props.sleeve.takeUniversityCourse(player, abc[2], abc[1]);
|
||||
break;
|
||||
case "Workout at Gym":
|
||||
props.sleeve.workoutAtGym(props.player, abc[2], abc[1]);
|
||||
props.sleeve.workoutAtGym(player, abc[2], abc[1]);
|
||||
break;
|
||||
case "Shock Recovery":
|
||||
props.sleeve.shockRecovery(props.player);
|
||||
props.sleeve.shockRecovery(player);
|
||||
break;
|
||||
case "Synchronize":
|
||||
props.sleeve.synchronize(props.player);
|
||||
props.sleeve.synchronize(player);
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid/Unrecognized taskValue in setSleeveTask(): ${abc[0]}`);
|
||||
@ -168,11 +149,6 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
||||
[`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 money={5 * props.sleeve.gainRatesForTask.money} />],
|
||||
@ -184,60 +160,68 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
||||
[`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);
|
||||
const repGain: number = props.sleeve.getRepGain(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">
|
||||
<>
|
||||
<Grid container component={Paper}>
|
||||
<Grid item xs={3}>
|
||||
<StatsElement sleeve={props.sleeve} />
|
||||
<button className="std-button" onClick={openMoreStats}>
|
||||
More Stats
|
||||
</button>
|
||||
<button
|
||||
className={`std-button${props.player.money.lt(CONSTANTS.TravelCost) ? " tooltip" : ""}`}
|
||||
onClick={openTravel}
|
||||
disabled={props.player.money.lt(CONSTANTS.TravelCost)}
|
||||
<Button onClick={() => setStatsOpen(true)}>More Stats</Button>
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={player.money.lt(CONSTANTS.TravelCost) ? <Typography>Insufficient funds</Typography> : ""}
|
||||
>
|
||||
Travel
|
||||
{props.player.money.lt(CONSTANTS.TravelCost) && <span className="tooltiptext">Not enough money</span>}
|
||||
</button>
|
||||
<button
|
||||
className={`std-button${props.sleeve.shock < 100 ? " tooltip" : ""}`}
|
||||
onClick={openManageAugmentations}
|
||||
style={{ display: "block" }}
|
||||
disabled={props.sleeve.shock < 100}
|
||||
<span>
|
||||
<Button onClick={() => setTravelOpen(true)} disabled={player.money.lt(CONSTANTS.TravelCost)}>
|
||||
Travel
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={props.sleeve.shock < 100 ? <Typography>Unlocked when sleeve has fully recovered</Typography> : ""}
|
||||
>
|
||||
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>
|
||||
<span>
|
||||
<Button onClick={() => setAugmentationsOpen(true)} disabled={props.sleeve.shock < 100}>
|
||||
Manage Augmentations
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
<Grid item xs={5}>
|
||||
<TaskSelector player={player} sleeve={props.sleeve} setABC={setABC} />
|
||||
<Typography>{desc}</Typography>
|
||||
<Typography>
|
||||
{props.sleeve.currentTask === SleeveTaskType.Crime &&
|
||||
createProgressBarText({
|
||||
progress: props.sleeve.currentTaskTime / props.sleeve.currentTaskMaxTime,
|
||||
totalTicks: 25,
|
||||
})}
|
||||
</Typography>
|
||||
<Button onClick={setTask}>Set Task</Button>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<StatsTable title="Earnings (Pre-Synchronization)" rows={data} />
|
||||
<Button onClick={() => setEarningsOpen(true)}>More Earnings Info</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<MoreStatsModal open={statsOpen} onClose={() => setStatsOpen(false)} sleeve={props.sleeve} />
|
||||
<MoreEarningsModal open={earningsOpen} onClose={() => setEarningsOpen(false)} sleeve={props.sleeve} />
|
||||
<TravelModal
|
||||
open={travelOpen}
|
||||
onClose={() => setTravelOpen(false)}
|
||||
sleeve={props.sleeve}
|
||||
rerender={props.rerender}
|
||||
/>
|
||||
<SleeveAugmentationsModal
|
||||
open={augmentationsOpen}
|
||||
onClose={() => setAugmentationsOpen(false)}
|
||||
sleeve={props.sleeve}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,28 +1,30 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
import { SleeveElem } from "./SleeveElem";
|
||||
import { FAQModal } from "./FAQModal";
|
||||
import { use } from "../../../ui/Context";
|
||||
|
||||
import { SleeveElem } from "../ui/SleeveElem";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import Link from "@mui/material/Link";
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
export function SleeveRoot(props: IProps): React.ReactElement {
|
||||
export function SleeveRoot(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const [FAQOpen, setFAQOpen] = useState(false);
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(rerender, 150);
|
||||
const id = setInterval(rerender, 200);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Sleeves</h1>
|
||||
<p>
|
||||
<Typography variant="h4">Sleeves</Typography>
|
||||
<Typography>
|
||||
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 />
|
||||
@ -30,26 +32,19 @@ export function SleeveRoot(props: IProps): React.ReactElement {
|
||||
Sleeves can be used to perform different tasks synchronously.
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
</Typography>
|
||||
|
||||
<button className="std-button" style={{ display: "inline-block" }}>
|
||||
FAQ
|
||||
</button>
|
||||
<a
|
||||
className="std-button"
|
||||
style={{ display: "inline-block" }}
|
||||
<Button onClick={() => setFAQOpen(true)}>FAQ</Button>
|
||||
<Link
|
||||
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>
|
||||
</Link>
|
||||
{player.sleeves.map((sleeve, i) => (
|
||||
<SleeveElem key={i} rerender={rerender} sleeve={sleeve} />
|
||||
))}
|
||||
<FAQModal open={FAQOpen} onClose={() => setFAQOpen(false)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,94 +1,31 @@
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import * as React from "react";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
||||
import { CONSTANTS } from "../../../Constants";
|
||||
import React from "react";
|
||||
|
||||
import { StatsTable } from "../../../ui/React/StatsTable";
|
||||
|
||||
interface IProps {
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
export function StatsElement(props: IProps): React.ReactElement {
|
||||
let style = {};
|
||||
style = { textAlign: "right" };
|
||||
return (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="character-hp-cell">HP: </td>
|
||||
<td className="character-hp-cell" style={style}>
|
||||
{numeralWrapper.formatHp(props.sleeve.hp)} / {numeralWrapper.formatHp(props.sleeve.max_hp)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>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(props.sleeve.hacking_skill)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-combat-cell">Strength: </td>
|
||||
<td className="character-combat-cell" style={style}>
|
||||
{numeralWrapper.formatSkill(props.sleeve.strength)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-combat-cell">Defense: </td>
|
||||
<td className="character-combat-cell" style={style}>
|
||||
{numeralWrapper.formatSkill(props.sleeve.defense)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-combat-cell">Dexterity: </td>
|
||||
<td className="character-combat-cell" style={style}>
|
||||
{numeralWrapper.formatSkill(props.sleeve.dexterity)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-combat-cell">Agility: </td>
|
||||
<td className="character-combat-cell" style={style}>
|
||||
{numeralWrapper.formatSkill(props.sleeve.agility)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-cha-cell">Charisma: </td>
|
||||
<td className="character-cha-cell" style={style}>
|
||||
{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 - props.sleeve.shock)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-int-cell">Sync: </td>
|
||||
<td className="character-int-cell" style={style}>
|
||||
{numeralWrapper.formatSleeveSynchro(props.sleeve.sync)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="character-int-cell">Memory: </td>
|
||||
<td className="character-int-cell" style={style}>
|
||||
{numeralWrapper.formatSleeveMemory(props.sleeve.memory)}
|
||||
</td>
|
||||
</tr>
|
||||
{props.sleeve.storedCycles > 15 && (
|
||||
<tr>
|
||||
<td>Bonus time: </td>
|
||||
<td style={style}>
|
||||
{convertTimeMsToTimeElapsedString((props.sleeve.storedCycles / (1000 / CONSTANTS._idleSpeed)) * 1000)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
const rows = [
|
||||
[
|
||||
"HP: ",
|
||||
<>
|
||||
{numeralWrapper.formatHp(props.sleeve.hp)} / {numeralWrapper.formatHp(props.sleeve.max_hp)}
|
||||
</>,
|
||||
],
|
||||
["City: ", <>{props.sleeve.city}</>],
|
||||
["Hacking: ", <>{numeralWrapper.formatSkill(props.sleeve.hacking_skill)}</>],
|
||||
["Strength: ", <>{numeralWrapper.formatSkill(props.sleeve.strength)}</>],
|
||||
["Defense: ", <>{numeralWrapper.formatSkill(props.sleeve.defense)}</>],
|
||||
["Dexterity: ", <>{numeralWrapper.formatSkill(props.sleeve.dexterity)}</>],
|
||||
["Agility: ", <>{numeralWrapper.formatSkill(props.sleeve.agility)}</>],
|
||||
["Charisma: ", <>{numeralWrapper.formatSkill(props.sleeve.charisma)}</>],
|
||||
["Shock: ", <>{numeralWrapper.formatSleeveShock(100 - props.sleeve.shock)}</>],
|
||||
["Sync: ", <>{numeralWrapper.formatSleeveSynchro(props.sleeve.sync)}</>],
|
||||
["Memory: ", <>{numeralWrapper.formatSleeveMemory(props.sleeve.memory)}</>],
|
||||
];
|
||||
return <StatsTable rows={rows} />;
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import { LocationName } from "../../../Locations/data/LocationNames";
|
||||
import { CityName } from "../../../Locations/data/CityNames";
|
||||
import { Factions } from "../../../Faction/Factions";
|
||||
import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
|
||||
const universitySelectorOptions: string[] = [
|
||||
"Study Computer Science",
|
||||
@ -249,7 +251,7 @@ export function TaskSelector(props: IProps): React.ReactElement {
|
||||
props.setABC([s0, s1, details2[0]]);
|
||||
}
|
||||
|
||||
function onS0Change(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
function onS0Change(event: SelectChangeEvent<string>): void {
|
||||
const n = event.target.value;
|
||||
const detailsF = tasks[n];
|
||||
if (detailsF === undefined) throw new Error(`No function for task '${s0}'`);
|
||||
@ -261,42 +263,48 @@ export function TaskSelector(props: IProps): React.ReactElement {
|
||||
props.setABC([n, details.first[0], details2[0]]);
|
||||
}
|
||||
|
||||
function onS1Change(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
function onS1Change(event: SelectChangeEvent<string>): void {
|
||||
setS1(event.target.value);
|
||||
props.setABC([s0, event.target.value, s2]);
|
||||
}
|
||||
|
||||
function onS2Change(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
function onS2Change(event: SelectChangeEvent<string>): void {
|
||||
setS2(event.target.value);
|
||||
props.setABC([s0, s1, event.target.value]);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<select className="dropdown" onChange={onS0Change} defaultValue={s0}>
|
||||
<Select variant="standard" onChange={onS0Change} value={s0}>
|
||||
{validActions.map((task) => (
|
||||
<option key={task} value={task}>
|
||||
<MenuItem key={task} value={task}>
|
||||
{task}
|
||||
</option>
|
||||
</MenuItem>
|
||||
))}
|
||||
</select>
|
||||
</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>
|
||||
<>
|
||||
<br />
|
||||
<Select variant="standard" onChange={onS1Change} value={s1}>
|
||||
{details.first.map((detail) => (
|
||||
<MenuItem key={detail} value={detail}>
|
||||
{detail}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</>
|
||||
)}
|
||||
{!(details2.length === 1 && details2[0] === "------") && (
|
||||
<select className="dropdown" onChange={onS2Change} defaultValue={s2}>
|
||||
{details2.map((detail) => (
|
||||
<option key={detail} value={detail}>
|
||||
{detail}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<>
|
||||
<br />
|
||||
<Select variant="standard" onChange={onS2Change} value={s2}>
|
||||
{details2.map((detail) => (
|
||||
<MenuItem key={detail} value={detail}>
|
||||
{detail}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
54
src/PersonObjects/Sleeve/ui/TravelModal.tsx
Normal file
54
src/PersonObjects/Sleeve/ui/TravelModal.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import React from "react";
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { CONSTANTS } from "../../../Constants";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import { WorldMap } from "../../../ui/React/WorldMap";
|
||||
import { CityName } from "../../../Locations/data/CityNames";
|
||||
import { Settings } from "../../../Settings/Settings";
|
||||
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
|
||||
import { use } from "../../../ui/Context";
|
||||
import { Modal } from "../../../ui/React/Modal";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
sleeve: Sleeve;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
export function TravelModal(props: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
function travel(city: string): void {
|
||||
if (!player.canAfford(CONSTANTS.TravelCost)) {
|
||||
dialogBoxCreate("You cannot afford to have this sleeve travel to another city");
|
||||
}
|
||||
props.sleeve.city = city as CityName;
|
||||
player.loseMoney(CONSTANTS.TravelCost);
|
||||
props.sleeve.resetTaskStatus();
|
||||
props.rerender();
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<>
|
||||
<Typography>
|
||||
Have this sleeve travel to a different city. This affects the gyms and universities at which this sleeve can
|
||||
study. Traveling to a different city costs <Money money={CONSTANTS.TravelCost} player={player} />. It will
|
||||
also set your current sleeve task to idle.
|
||||
</Typography>
|
||||
{Settings.DisableASCIIArt ? (
|
||||
Object.values(CityName).map((city: CityName) => (
|
||||
<Button key={city} onClick={() => travel(city)}>
|
||||
{city}
|
||||
</Button>
|
||||
))
|
||||
) : (
|
||||
<WorldMap currentCity={props.sleeve.city} onTravel={(city: CityName) => travel(city)} />
|
||||
)}
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
import React from "react";
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
import { CONSTANTS } from "../../../Constants";
|
||||
import { removePopup } from "../../../ui/React/createPopup";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import { WorldMap } from "../../../ui/React/WorldMap";
|
||||
import { CityName } from "../../../Locations/data/CityNames";
|
||||
import { Settings } from "../../../Settings/Settings";
|
||||
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
|
||||
|
||||
interface IProps {
|
||||
popupId: string;
|
||||
sleeve: Sleeve;
|
||||
player: IPlayer;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
export function TravelPopup(props: IProps): React.ReactElement {
|
||||
function travel(city: string): void {
|
||||
if (!props.player.canAfford(CONSTANTS.TravelCost)) {
|
||||
dialogBoxCreate("You cannot afford to have this sleeve travel to another city");
|
||||
}
|
||||
props.sleeve.city = city as CityName;
|
||||
props.player.loseMoney(CONSTANTS.TravelCost);
|
||||
props.sleeve.resetTaskStatus();
|
||||
removePopup(props.popupId);
|
||||
props.rerender();
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
Have this sleeve travel to a different city. This affects the gyms and universities at which this sleeve can
|
||||
study. Traveling to a different city costs <Money money={CONSTANTS.TravelCost} player={props.player} />. It will
|
||||
also set your current sleeve task to idle.
|
||||
</p>
|
||||
{Settings.DisableASCIIArt ? (
|
||||
Object.values(CityName).map((city: CityName) => (
|
||||
<button key={city} className="std-button" onClick={() => travel(city)}>
|
||||
{city}
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<WorldMap currentCity={props.sleeve.city} onTravel={(city: CityName) => travel(city)} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
import { use } from "../../ui/Context";
|
||||
import { getAvailableCreatePrograms } from "../ProgramHelpers";
|
||||
|
||||
import { Box, Tooltip, Typography } from "@mui/material";
|
||||
import { Tooltip, Typography } from "@mui/material";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
export function ProgramsRoot(): React.ReactElement {
|
||||
|
@ -306,7 +306,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
{page === Page.Terminal ? (
|
||||
<TerminalRoot terminal={terminal} router={Router} player={player} />
|
||||
) : page === Page.Sleeves ? (
|
||||
<SleeveRoot player={player} />
|
||||
<SleeveRoot />
|
||||
) : page === Page.Stats ? (
|
||||
<CharacterStats />
|
||||
) : page === Page.CreateScript ? (
|
||||
|
@ -19,8 +19,8 @@ export function StatsTable({ rows, title, wide }: IProps): React.ReactElement {
|
||||
{title && <Typography>{title}</Typography>}
|
||||
<T size="small" padding="none">
|
||||
<TableBody>
|
||||
{rows.map((row: any[]) => (
|
||||
<TableRow key={row[0]}>
|
||||
{rows.map((row: any[], i: number) => (
|
||||
<TableRow key={i}>
|
||||
{row.map((elem: any, i: number) => (
|
||||
<TableCell key={i} align={i !== 0 ? "right" : "left"}>
|
||||
<Typography noWrap>{elem}</Typography>
|
||||
|
Loading…
Reference in New Issue
Block a user