mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-26 01:23:49 +01:00
100% mui I think
This commit is contained in:
parent
1e641468f7
commit
0ae8b72188
@ -10,7 +10,6 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import Tabs from "@mui/material/Tabs";
|
||||
import Tab from "@mui/material/Tab";
|
||||
import Box from "@mui/material/Box";
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
|
@ -38,7 +38,7 @@ export interface IGang {
|
||||
getWantedPenalty(): number;
|
||||
calculatePower(): number;
|
||||
killMember(member: GangMember): void;
|
||||
ascendMember(member: GangMember, workerScript: WorkerScript): IAscensionResult;
|
||||
ascendMember(member: GangMember, workerScript?: WorkerScript): IAscensionResult;
|
||||
getDiscount(): number;
|
||||
getAllTaskNames(): string[];
|
||||
getUpgradeCost(upg: GangMemberUpgrade): number;
|
||||
|
@ -3,20 +3,23 @@
|
||||
* ascension of a gang member.
|
||||
*/
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Gang } from "../Gang";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { useGang } from "./Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
member: GangMember;
|
||||
gang: Gang;
|
||||
popupId: string;
|
||||
onAscend: () => void;
|
||||
}
|
||||
|
||||
export function AscensionPopup(props: IProps): React.ReactElement {
|
||||
export function AscensionModal(props: IProps): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const setRerender = useState(false)[1];
|
||||
|
||||
useEffect(() => {
|
||||
@ -26,9 +29,9 @@ export function AscensionPopup(props: IProps): React.ReactElement {
|
||||
|
||||
function confirm(): void {
|
||||
props.onAscend();
|
||||
const res = props.gang.ascendMember(props.member);
|
||||
const res = gang.ascendMember(props.member);
|
||||
dialogBoxCreate(
|
||||
<p>
|
||||
<Typography>
|
||||
You ascended {props.member.name}!<br />
|
||||
<br />
|
||||
Your gang lost {numeralWrapper.formatRespect(res.respect)} respect.
|
||||
@ -48,13 +51,13 @@ export function AscensionPopup(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
Charisma: x{numeralWrapper.format(res.cha, "0.000")}
|
||||
<br />
|
||||
</p>,
|
||||
</Typography>,
|
||||
);
|
||||
removePopup(props.popupId);
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
function cancel(): void {
|
||||
removePopup(props.popupId);
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
// const ascendBenefits = props.member.getAscensionResults();
|
||||
@ -62,8 +65,8 @@ export function AscensionPopup(props: IProps): React.ReactElement {
|
||||
const postAscend = props.member.getAscensionMultsAfterAscend();
|
||||
|
||||
return (
|
||||
<>
|
||||
<pre>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography>
|
||||
Are you sure you want to ascend this member? They will lose all of
|
||||
<br />
|
||||
their non-Augmentation upgrades and their stats will reset back to 1.
|
||||
@ -92,13 +95,8 @@ export function AscensionPopup(props: IProps): React.ReactElement {
|
||||
Charisma: x{numeralWrapper.format(preAscend.cha, "0.000")} => x
|
||||
{numeralWrapper.format(postAscend.cha, "0.000")}
|
||||
<br />
|
||||
</pre>
|
||||
<button className="std-button" onClick={confirm}>
|
||||
Ascend
|
||||
</button>
|
||||
<button className="std-button" onClick={cancel}>
|
||||
Cancel
|
||||
</button>
|
||||
</>
|
||||
</Typography>
|
||||
<Button onClick={confirm}>Ascend</Button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -5,6 +5,9 @@ import * as React from "react";
|
||||
import { Gang } from "../Gang";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
interface IProps {
|
||||
gang: Gang;
|
||||
@ -15,15 +18,17 @@ export function BonusTime(props: IProps): React.ReactElement {
|
||||
if ((props.gang.storedCycles / CyclerPerSecond) * 1000 <= 5000) return <></>;
|
||||
const bonusMillis = (props.gang.storedCycles / CyclerPerSecond) * 1000;
|
||||
return (
|
||||
<>
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Bonus time: {convertTimeMsToTimeElapsedString(bonusMillis)}
|
||||
<span className="tooltiptext noselect">
|
||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the
|
||||
browser). Bonus time makes the Gang mechanic progress faster, up to 5x the normal speed.
|
||||
</span>
|
||||
</p>
|
||||
<br />
|
||||
</>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the
|
||||
browser). Bonus time makes the Gang mechanic progress faster, up to 5x the normal speed.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Bonus time: {convertTimeMsToTimeElapsedString(bonusMillis)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
10
src/Gang/ui/Context.ts
Normal file
10
src/Gang/ui/Context.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import React, { useContext } from "react";
|
||||
import { IGang } from "../IGang";
|
||||
|
||||
export const Context: {
|
||||
Gang: React.Context<IGang>;
|
||||
} = {
|
||||
Gang: React.createContext<IGang>({} as IGang),
|
||||
};
|
||||
|
||||
export const useGang = () => useContext(Context.Gang);
|
220
src/Gang/ui/EquipmentsSubpage.tsx
Normal file
220
src/Gang/ui/EquipmentsSubpage.tsx
Normal file
@ -0,0 +1,220 @@
|
||||
/**
|
||||
* React Component for the popup that manages gang members upgrades
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { GangMemberUpgrades } from "../GangMemberUpgrades";
|
||||
import { GangMemberUpgrade } from "../GangMemberUpgrade";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { useGang } from "./Context";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { UpgradeType } from "../data/upgrades";
|
||||
import { use } from "../../ui/Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Grid from "@mui/material/Grid";
|
||||
|
||||
interface INextRevealProps {
|
||||
upgrades: string[];
|
||||
type: UpgradeType;
|
||||
}
|
||||
|
||||
function NextReveal(props: INextRevealProps): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const player = use.Player();
|
||||
const upgrades = Object.keys(GangMemberUpgrades)
|
||||
.filter((upgName: string) => {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
if (player.money.gt(gang.getUpgradeCost(upg))) return false;
|
||||
if (upg.type !== props.type) return false;
|
||||
if (props.upgrades.includes(upgName)) return false;
|
||||
return true;
|
||||
})
|
||||
.map((upgName: string) => GangMemberUpgrades[upgName]);
|
||||
|
||||
if (upgrades.length === 0) return <></>;
|
||||
return (
|
||||
<Typography>
|
||||
Next at <Money money={upgrades[0].cost} />
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
function PurchasedUpgrade({ upgName }: { upgName: string }): React.ReactElement {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
return (
|
||||
<Paper sx={{ mx: 1, p: 1 }}>
|
||||
<Box display="flex">
|
||||
<Tooltip title={<Typography dangerouslySetInnerHTML={{ __html: upg.desc }} />}>
|
||||
<Typography>{upg.name}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
interface IUpgradeButtonProps {
|
||||
upg: GangMemberUpgrade;
|
||||
rerender: () => void;
|
||||
member: GangMember;
|
||||
}
|
||||
|
||||
function UpgradeButton(props: IUpgradeButtonProps): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const player = use.Player();
|
||||
function onClick(): void {
|
||||
props.member.buyUpgrade(props.upg, player, gang);
|
||||
props.rerender();
|
||||
}
|
||||
return (
|
||||
<Tooltip title={<Typography dangerouslySetInnerHTML={{ __html: props.upg.desc }} />}>
|
||||
<span>
|
||||
<Typography>{props.upg.name}</Typography>
|
||||
<Button onClick={onClick}>
|
||||
<Money money={gang.getUpgradeCost(props.upg)} />
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
interface IPanelProps {
|
||||
member: GangMember;
|
||||
}
|
||||
|
||||
function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const player = use.Player();
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
function filterUpgrades(list: string[], type: UpgradeType): GangMemberUpgrade[] {
|
||||
return Object.keys(GangMemberUpgrades)
|
||||
.filter((upgName: string) => {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
if (player.money.lt(gang.getUpgradeCost(upg))) return false;
|
||||
if (upg.type !== type) return false;
|
||||
if (list.includes(upgName)) return false;
|
||||
return true;
|
||||
})
|
||||
.map((upgName: string) => GangMemberUpgrades[upgName]);
|
||||
}
|
||||
const weaponUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Weapon);
|
||||
const armorUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Armor);
|
||||
const vehicleUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Vehicle);
|
||||
const rootkitUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Rootkit);
|
||||
const augUpgrades = filterUpgrades(props.member.augmentations, UpgradeType.Augmentation);
|
||||
|
||||
const asc = {
|
||||
hack: props.member.calculateAscensionMult(props.member.hack_asc_points),
|
||||
str: props.member.calculateAscensionMult(props.member.str_asc_points),
|
||||
def: props.member.calculateAscensionMult(props.member.def_asc_points),
|
||||
dex: props.member.calculateAscensionMult(props.member.dex_asc_points),
|
||||
agi: props.member.calculateAscensionMult(props.member.agi_asc_points),
|
||||
cha: props.member.calculateAscensionMult(props.member.cha_asc_points),
|
||||
};
|
||||
return (
|
||||
<Paper>
|
||||
<Typography variant="h5" color="primary">
|
||||
{props.member.name} ({props.member.task})
|
||||
</Typography>
|
||||
<Typography>
|
||||
Hack: {props.member.hack} (x
|
||||
{formatNumber(props.member.hack_mult * asc.hack, 2)})<br />
|
||||
Str: {props.member.str} (x
|
||||
{formatNumber(props.member.str_mult * asc.str, 2)})<br />
|
||||
Def: {props.member.def} (x
|
||||
{formatNumber(props.member.def_mult * asc.def, 2)})<br />
|
||||
Dex: {props.member.dex} (x
|
||||
{formatNumber(props.member.dex_mult * asc.dex, 2)})<br />
|
||||
Agi: {props.member.agi} (x
|
||||
{formatNumber(props.member.agi_mult * asc.agi, 2)})<br />
|
||||
Cha: {props.member.cha} (x
|
||||
{formatNumber(props.member.cha_mult * asc.cha, 2)})
|
||||
</Typography>
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
<Typography>Purchased Upgrades: </Typography>
|
||||
<br />
|
||||
{props.member.upgrades.map((upg: string) => (
|
||||
<PurchasedUpgrade key={upg} upgName={upg} />
|
||||
))}
|
||||
{props.member.augmentations.map((upg: string) => (
|
||||
<PurchasedUpgrade key={upg} upgName={upg} />
|
||||
))}
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="space-around">
|
||||
<Box>
|
||||
<Typography variant="h6" color="primary">
|
||||
Weapons
|
||||
</Typography>
|
||||
{weaponUpgrades.map((upg) => (
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
<NextReveal type={UpgradeType.Weapon} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h6" color="primary">
|
||||
Armor
|
||||
</Typography>
|
||||
{armorUpgrades.map((upg) => (
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
<NextReveal type={UpgradeType.Armor} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h6" color="primary">
|
||||
Vehicles
|
||||
</Typography>
|
||||
{vehicleUpgrades.map((upg) => (
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
<NextReveal type={UpgradeType.Vehicle} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h6" color="primary">
|
||||
Rootkits
|
||||
</Typography>
|
||||
{rootkitUpgrades.map((upg) => (
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
<NextReveal type={UpgradeType.Rootkit} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h6" color="primary">
|
||||
Augmentations
|
||||
</Typography>
|
||||
{augUpgrades.map((upg) => (
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
<NextReveal type={UpgradeType.Augmentation} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
export function EquipmentsSubpage(): React.ReactElement {
|
||||
const gang = useGang();
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
You get a discount on equipment and upgrades based on your gang's respect and power. More respect and power
|
||||
leads to more discounts.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Discount: -{numeralWrapper.formatPercentage(1 - 1 / gang.getDiscount())}</Typography>
|
||||
</Tooltip>
|
||||
{gang.members.map((member: GangMember) => (
|
||||
<GangMemberUpgradePanel key={member.name} member={member} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,23 +1,36 @@
|
||||
/**
|
||||
* React Component for a gang member on the management subpage.
|
||||
*/
|
||||
import React from "react";
|
||||
import { Gang } from "../Gang";
|
||||
import React, { useState } from "react";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { BBAccordion } from "../../ui/React/BBAccordion";
|
||||
import { GangMemberAccordionContent } from "./GangMemberAccordionContent";
|
||||
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import ExpandMore from "@mui/icons-material/ExpandMore";
|
||||
import ExpandLess from "@mui/icons-material/ExpandLess";
|
||||
interface IProps {
|
||||
gang: Gang;
|
||||
member: GangMember;
|
||||
}
|
||||
|
||||
export function GangMemberAccordion(props: IProps): React.ReactElement {
|
||||
const [open, setOpen] = useState(true);
|
||||
return (
|
||||
<BBAccordion
|
||||
panelInitiallyOpened={true}
|
||||
headerContent={<>{props.member.name}</>}
|
||||
panelContent={<GangMemberAccordionContent gang={props.gang} member={props.member} />}
|
||||
/>
|
||||
<Box component={Paper}>
|
||||
<ListItemButton onClick={() => setOpen((old) => !old)}>
|
||||
<ListItemText primary={<Typography>{props.member.name}</Typography>} />
|
||||
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||
</ListItemButton>
|
||||
<Collapse in={open} unmountOnExit>
|
||||
<Box sx={{ mx: 4 }}>
|
||||
<GangMemberAccordionContent member={props.member} />
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -6,27 +6,26 @@ import React, { useState } from "react";
|
||||
import { GangMemberStats } from "./GangMemberStats";
|
||||
import { TaskSelector } from "./TaskSelector";
|
||||
import { TaskDescription } from "./TaskDescription";
|
||||
import { Gang } from "../Gang";
|
||||
import { GangMember } from "../GangMember";
|
||||
import Grid from "@mui/material/Grid";
|
||||
|
||||
interface IProps {
|
||||
gang: Gang;
|
||||
member: GangMember;
|
||||
}
|
||||
|
||||
export function GangMemberAccordionContent(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
return (
|
||||
<>
|
||||
<div className={"gang-member-info-div tooltip"}>
|
||||
<GangMemberStats onAscend={() => setRerender((old) => !old)} gang={props.gang} member={props.member} />
|
||||
</div>
|
||||
<div className={"gang-member-info-div"}>
|
||||
<TaskSelector onTaskChange={() => setRerender((old) => !old)} gang={props.gang} member={props.member} />
|
||||
</div>
|
||||
<div className={"gang-member-info-div"}>
|
||||
<Grid container>
|
||||
<Grid item xs={4}>
|
||||
<GangMemberStats onAscend={() => setRerender((old) => !old)} member={props.member} />
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<TaskSelector onTaskChange={() => setRerender((old) => !old)} member={props.member} />
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<TaskDescription member={props.member} />
|
||||
</div>
|
||||
</>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
@ -2,59 +2,21 @@
|
||||
* React Component for the list of gang members on the management subpage.
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { GangMemberUpgradePopup } from "./GangMemberUpgradePopup";
|
||||
import { GangMemberAccordion } from "./GangMemberAccordion";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Gang } from "../Gang";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { RecruitButton } from "./RecruitButton";
|
||||
import { useGang } from "./Context";
|
||||
|
||||
interface IProps {
|
||||
gang: Gang;
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
export function GangMemberList(props: IProps): React.ReactElement {
|
||||
const [filter, setFilter] = useState("");
|
||||
export function GangMemberList(): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const setRerender = useState(false)[1];
|
||||
|
||||
function openUpgradePopup(): void {
|
||||
const popupId = `gang-upgrade-popup`;
|
||||
createPopup(popupId, GangMemberUpgradePopup, {
|
||||
gang: props.gang,
|
||||
player: props.player,
|
||||
popupId: popupId,
|
||||
});
|
||||
}
|
||||
|
||||
function onFilterChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setFilter(event.target.value);
|
||||
}
|
||||
|
||||
const members = props.gang.members.filter(
|
||||
(member: GangMember) => member.name.indexOf(filter) > -1 || member.task.indexOf(filter) > -1,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<RecruitButton onRecruit={() => setRerender((old) => !old)} gang={props.gang} />
|
||||
<br />
|
||||
<input
|
||||
className="text-input noselect"
|
||||
placeholder="Filter gang member"
|
||||
style={{ margin: "5px", padding: "5px" }}
|
||||
value={filter}
|
||||
onChange={onFilterChange}
|
||||
/>
|
||||
<a className="a-link-button" style={{ display: "inline-block" }} onClick={openUpgradePopup}>
|
||||
Manage Equipment
|
||||
</a>
|
||||
<RecruitButton onRecruit={() => setRerender((old) => !old)} />
|
||||
<ul>
|
||||
{members.map((member: GangMember) => (
|
||||
<li key={member.name}>
|
||||
<GangMemberAccordion gang={props.gang} member={member} />
|
||||
</li>
|
||||
{gang.members.map((member: GangMember) => (
|
||||
<GangMemberAccordion key={member.name} member={member} />
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
|
@ -2,48 +2,26 @@
|
||||
* React Component for the first part of a gang member details.
|
||||
* Contains skills and exp.
|
||||
*/
|
||||
import React from "react";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import React, { useState } from "react";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { Gang } from "../Gang";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { AscensionPopup } from "./AscensionPopup";
|
||||
import { AscensionModal } from "./AscensionModal";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Button from "@mui/material/Button";
|
||||
import { StaticModal } from "../../ui/React/StaticModal";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import HelpIcon from "@mui/icons-material/Help";
|
||||
|
||||
interface IProps {
|
||||
member: GangMember;
|
||||
gang: Gang;
|
||||
onAscend: () => void;
|
||||
}
|
||||
|
||||
export function GangMemberStats(props: IProps): React.ReactElement {
|
||||
function ascend(): void {
|
||||
const popupId = `gang-management-ascend-member ${props.member.name}`;
|
||||
createPopup(popupId, AscensionPopup, {
|
||||
member: props.member,
|
||||
gang: props.gang,
|
||||
popupId: popupId,
|
||||
onAscend: props.onAscend,
|
||||
});
|
||||
}
|
||||
|
||||
function openAscensionHelp(): void {
|
||||
dialogBoxCreate(
|
||||
<>
|
||||
Ascending a Gang Member resets the member's progress and stats in exchange for a permanent boost to their stat
|
||||
multipliers.
|
||||
<br />
|
||||
<br />
|
||||
The additional stat multiplier that the Gang Member gains upon ascension is based on the amount of exp they
|
||||
have.
|
||||
<br />
|
||||
<br />
|
||||
Upon ascension, the member will lose all of its non-Augmentation Equipment and your gang will lose respect equal
|
||||
to the total respect earned by the member.
|
||||
</>,
|
||||
);
|
||||
}
|
||||
const [helpOpen, setHelpOpen] = useState(false);
|
||||
const [ascendOpen, setAscendOpen] = useState(false);
|
||||
|
||||
const asc = {
|
||||
hack: props.member.calculateAscensionMult(props.member.hack_asc_points),
|
||||
@ -56,48 +34,77 @@ export function GangMemberStats(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="tooltiptext smallfont">
|
||||
Hk: x{numeralWrapper.formatMultiplier(props.member.hack_mult * asc.hack)}(x
|
||||
{numeralWrapper.formatMultiplier(props.member.hack_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.hack)} Asc)
|
||||
<br />
|
||||
St: x{numeralWrapper.formatMultiplier(props.member.str_mult * asc.str)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.str_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.str)} Asc)
|
||||
<br />
|
||||
Df: x{numeralWrapper.formatMultiplier(props.member.def_mult * asc.def)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.def_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.def)} Asc)
|
||||
<br />
|
||||
Dx: x{numeralWrapper.formatMultiplier(props.member.dex_mult * asc.dex)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.dex_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.dex)} Asc)
|
||||
<br />
|
||||
Ag: x{numeralWrapper.formatMultiplier(props.member.agi_mult * asc.agi)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.agi_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.agi)} Asc)
|
||||
<br />
|
||||
Ch: x{numeralWrapper.formatMultiplier(props.member.cha_mult * asc.cha)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.cha_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.cha)} Asc)
|
||||
</span>
|
||||
<pre>
|
||||
Hacking: {formatNumber(props.member.hack, 0)} ({numeralWrapper.formatExp(props.member.hack_exp)} exp)
|
||||
<br />
|
||||
Strength: {formatNumber(props.member.str, 0)} ({numeralWrapper.formatExp(props.member.str_exp)} exp)
|
||||
<br />
|
||||
Defense: {formatNumber(props.member.def, 0)} ({numeralWrapper.formatExp(props.member.def_exp)} exp)
|
||||
<br />
|
||||
Dexterity: {formatNumber(props.member.dex, 0)} ({numeralWrapper.formatExp(props.member.dex_exp)} exp)
|
||||
<br />
|
||||
Agility: {formatNumber(props.member.agi, 0)} ({numeralWrapper.formatExp(props.member.agi_exp)} exp)
|
||||
<br />
|
||||
Charisma: {formatNumber(props.member.cha, 0)} ({numeralWrapper.formatExp(props.member.cha_exp)} exp)
|
||||
<br />
|
||||
</pre>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Hk: x{numeralWrapper.formatMultiplier(props.member.hack_mult * asc.hack)}(x
|
||||
{numeralWrapper.formatMultiplier(props.member.hack_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.hack)}{" "}
|
||||
Asc)
|
||||
<br />
|
||||
St: x{numeralWrapper.formatMultiplier(props.member.str_mult * asc.str)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.str_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.str)}{" "}
|
||||
Asc)
|
||||
<br />
|
||||
Df: x{numeralWrapper.formatMultiplier(props.member.def_mult * asc.def)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.def_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.def)}{" "}
|
||||
Asc)
|
||||
<br />
|
||||
Dx: x{numeralWrapper.formatMultiplier(props.member.dex_mult * asc.dex)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.dex_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.dex)}{" "}
|
||||
Asc)
|
||||
<br />
|
||||
Ag: x{numeralWrapper.formatMultiplier(props.member.agi_mult * asc.agi)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.agi_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.agi)}{" "}
|
||||
Asc)
|
||||
<br />
|
||||
Ch: x{numeralWrapper.formatMultiplier(props.member.cha_mult * asc.cha)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.cha_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.cha)}{" "}
|
||||
Asc)
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Hacking: {formatNumber(props.member.hack, 0)} ({numeralWrapper.formatExp(props.member.hack_exp)} exp)
|
||||
<br />
|
||||
Strength: {formatNumber(props.member.str, 0)} ({numeralWrapper.formatExp(props.member.str_exp)} exp)
|
||||
<br />
|
||||
Defense: {formatNumber(props.member.def, 0)} ({numeralWrapper.formatExp(props.member.def_exp)} exp)
|
||||
<br />
|
||||
Dexterity: {formatNumber(props.member.dex, 0)} ({numeralWrapper.formatExp(props.member.dex_exp)} exp)
|
||||
<br />
|
||||
Agility: {formatNumber(props.member.agi, 0)} ({numeralWrapper.formatExp(props.member.agi_exp)} exp)
|
||||
<br />
|
||||
Charisma: {formatNumber(props.member.cha, 0)} ({numeralWrapper.formatExp(props.member.cha_exp)} exp)
|
||||
<br />
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
<br />
|
||||
{props.member.canAscend() && (
|
||||
<>
|
||||
<button className="accordion-button noselect" onClick={ascend}>
|
||||
Ascend
|
||||
</button>
|
||||
<div className="help-tip noselect" style={{ marginTop: "5px" }} onClick={openAscensionHelp}>
|
||||
?
|
||||
</div>
|
||||
<Button onClick={() => setAscendOpen(true)}>Ascend</Button>
|
||||
<AscensionModal
|
||||
open={ascendOpen}
|
||||
onClose={() => setAscendOpen(false)}
|
||||
member={props.member}
|
||||
onAscend={props.onAscend}
|
||||
/>
|
||||
<IconButton onClick={() => setHelpOpen(true)}>
|
||||
<HelpIcon />
|
||||
</IconButton>
|
||||
<StaticModal open={helpOpen} onClose={() => setHelpOpen(false)}>
|
||||
<Typography>
|
||||
Ascending a Gang Member resets the member's progress and stats in exchange for a permanent boost to their
|
||||
stat multipliers.
|
||||
<br />
|
||||
<br />
|
||||
The additional stat multiplier that the Gang Member gains upon ascension is based on the amount of exp
|
||||
they have.
|
||||
<br />
|
||||
<br />
|
||||
Upon ascension, the member will lose all of its non-Augmentation Equipment and your gang will lose respect
|
||||
equal to the total respect earned by the member.
|
||||
</Typography>
|
||||
</StaticModal>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
@ -1,224 +0,0 @@
|
||||
/**
|
||||
* React Component for the popup that manages gang members upgrades
|
||||
*/
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { GangMemberUpgrades } from "../GangMemberUpgrades";
|
||||
import { GangMemberUpgrade } from "../GangMemberUpgrade";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { Gang } from "../Gang";
|
||||
import { UpgradeType } from "../data/upgrades";
|
||||
|
||||
interface INextRevealProps {
|
||||
gang: Gang;
|
||||
upgrades: string[];
|
||||
type: UpgradeType;
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
function NextReveal(props: INextRevealProps): React.ReactElement {
|
||||
const upgrades = Object.keys(GangMemberUpgrades)
|
||||
.filter((upgName: string) => {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
if (props.player.money.gt(props.gang.getUpgradeCost(upg))) return false;
|
||||
if (upg.type !== props.type) return false;
|
||||
if (props.upgrades.includes(upgName)) return false;
|
||||
return true;
|
||||
})
|
||||
.map((upgName: string) => GangMemberUpgrades[upgName]);
|
||||
|
||||
if (upgrades.length === 0) return <></>;
|
||||
return (
|
||||
<p>
|
||||
Next at <Money money={upgrades[0].cost} />
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
interface IPanelProps {
|
||||
member: GangMember;
|
||||
gang: Gang;
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function filterUpgrades(list: string[], type: UpgradeType): GangMemberUpgrade[] {
|
||||
return Object.keys(GangMemberUpgrades)
|
||||
.filter((upgName: string) => {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
if (props.player.money.lt(props.gang.getUpgradeCost(upg))) return false;
|
||||
if (upg.type !== type) return false;
|
||||
if (list.includes(upgName)) return false;
|
||||
return true;
|
||||
})
|
||||
.map((upgName: string) => GangMemberUpgrades[upgName]);
|
||||
}
|
||||
const weaponUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Weapon);
|
||||
const armorUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Armor);
|
||||
const vehicleUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Vehicle);
|
||||
const rootkitUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Rootkit);
|
||||
const augUpgrades = filterUpgrades(props.member.augmentations, UpgradeType.Augmentation);
|
||||
|
||||
function purchasedUpgrade(upgName: string): React.ReactElement {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
return (
|
||||
<div key={upgName} className="gang-owned-upgrade tooltip">
|
||||
{upg.name}
|
||||
<span className="tooltiptext" dangerouslySetInnerHTML={{ __html: upg.desc }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function upgradeButton(upg: GangMemberUpgrade, left = false): React.ReactElement {
|
||||
function onClick(): void {
|
||||
props.member.buyUpgrade(upg, props.player, props.gang);
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
return (
|
||||
<a
|
||||
key={upg.name}
|
||||
className="a-link-button tooltip"
|
||||
style={{
|
||||
margin: "2px",
|
||||
padding: "2px",
|
||||
display: "block",
|
||||
fontSize: "11px",
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
{upg.name} - <Money money={props.gang.getUpgradeCost(upg)} player={props.player} />
|
||||
<span className={left ? "tooltiptextleft" : "tooltiptext"} dangerouslySetInnerHTML={{ __html: upg.desc }} />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
const asc = {
|
||||
hack: props.member.calculateAscensionMult(props.member.hack_asc_points),
|
||||
str: props.member.calculateAscensionMult(props.member.str_asc_points),
|
||||
def: props.member.calculateAscensionMult(props.member.def_asc_points),
|
||||
dex: props.member.calculateAscensionMult(props.member.dex_asc_points),
|
||||
agi: props.member.calculateAscensionMult(props.member.agi_asc_points),
|
||||
cha: props.member.calculateAscensionMult(props.member.cha_asc_points),
|
||||
};
|
||||
return (
|
||||
<div style={{ border: "1px solid white" }}>
|
||||
<h1>
|
||||
{props.member.name}({props.member.task})
|
||||
</h1>
|
||||
<pre style={{ fontSize: "14px", display: "inline-block", width: "20%" }}>
|
||||
Hack: {props.member.hack} (x
|
||||
{formatNumber(props.member.hack_mult * asc.hack, 2)})<br />
|
||||
Str: {props.member.str} (x
|
||||
{formatNumber(props.member.str_mult * asc.str, 2)})<br />
|
||||
Def: {props.member.def} (x
|
||||
{formatNumber(props.member.def_mult * asc.def, 2)})<br />
|
||||
Dex: {props.member.dex} (x
|
||||
{formatNumber(props.member.dex_mult * asc.dex, 2)})<br />
|
||||
Agi: {props.member.agi} (x
|
||||
{formatNumber(props.member.agi_mult * asc.agi, 2)})<br />
|
||||
Cha: {props.member.cha} (x
|
||||
{formatNumber(props.member.cha_mult * asc.cha, 2)})
|
||||
</pre>
|
||||
<div className="gang-owned-upgrades-div noselect">
|
||||
Purchased Upgrades: {props.member.upgrades.map((upg: string) => purchasedUpgrade(upg))}
|
||||
{props.member.augmentations.map((upg: string) => purchasedUpgrade(upg))}
|
||||
</div>
|
||||
<div className="noselect" style={{ width: "20%", display: "inline-block" }}>
|
||||
<h2>Weapons</h2>
|
||||
{weaponUpgrades.map((upg) => upgradeButton(upg))}
|
||||
<NextReveal
|
||||
gang={props.gang}
|
||||
type={UpgradeType.Weapon}
|
||||
player={props.player}
|
||||
upgrades={props.member.upgrades}
|
||||
/>
|
||||
</div>
|
||||
<div className="noselect" style={{ width: "20%", display: "inline-block" }}>
|
||||
<h2>Armor</h2>
|
||||
{armorUpgrades.map((upg) => upgradeButton(upg))}
|
||||
<NextReveal gang={props.gang} type={UpgradeType.Armor} player={props.player} upgrades={props.member.upgrades} />
|
||||
</div>
|
||||
<div className="noselect" style={{ width: "20%", display: "inline-block" }}>
|
||||
<h2>Vehicles</h2>
|
||||
{vehicleUpgrades.map((upg) => upgradeButton(upg))}
|
||||
<NextReveal
|
||||
gang={props.gang}
|
||||
type={UpgradeType.Vehicle}
|
||||
player={props.player}
|
||||
upgrades={props.member.upgrades}
|
||||
/>
|
||||
</div>
|
||||
<div className="noselect" style={{ width: "20%", display: "inline-block" }}>
|
||||
<h2>Rootkits</h2>
|
||||
{rootkitUpgrades.map((upg) => upgradeButton(upg, true))}
|
||||
<NextReveal
|
||||
gang={props.gang}
|
||||
type={UpgradeType.Rootkit}
|
||||
player={props.player}
|
||||
upgrades={props.member.upgrades}
|
||||
/>
|
||||
</div>
|
||||
<div className="noselect" style={{ width: "20%", display: "inline-block" }}>
|
||||
<h2>Augmentations</h2>
|
||||
{augUpgrades.map((upg) => upgradeButton(upg, true))}
|
||||
<NextReveal
|
||||
gang={props.gang}
|
||||
type={UpgradeType.Augmentation}
|
||||
player={props.player}
|
||||
upgrades={props.member.upgrades}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
gang: Gang;
|
||||
player: IPlayer;
|
||||
popupId: string;
|
||||
}
|
||||
|
||||
export function GangMemberUpgradePopup(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
const [filter, setFilter] = useState("");
|
||||
|
||||
function closePopup(this: Window, ev: KeyboardEvent): void {
|
||||
if (ev.keyCode !== 27) return;
|
||||
removePopup(props.popupId);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener<"keydown">("keydown", closePopup);
|
||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
window.removeEventListener<"keydown">("keydown", closePopup);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
className="text-input noselect"
|
||||
value={filter}
|
||||
placeholder="Filter gang member"
|
||||
onChange={(event) => setFilter(event.target.value)}
|
||||
/>
|
||||
<p className="tooltip" style={{ marginLeft: "6px", display: "inline-block" }}>
|
||||
Discount: -{numeralWrapper.formatPercentage(1 - 1 / props.gang.getDiscount())}
|
||||
<span className="tooltiptext noselect">
|
||||
You get a discount on equipment and upgrades based on your gang's respect and power. More respect and power
|
||||
leads to more discounts.
|
||||
</span>
|
||||
</p>
|
||||
{props.gang.members.map((member: GangMember) => (
|
||||
<GangMemberUpgradePanel key={member.name} player={props.player} gang={props.gang} member={member} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
@ -4,48 +4,49 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { ManagementSubpage } from "./ManagementSubpage";
|
||||
import { TerritorySubpage } from "./TerritorySubpage";
|
||||
import { EquipmentsSubpage } from "./EquipmentsSubpage";
|
||||
import { use } from "../../ui/Context";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import { Context } from "./Context";
|
||||
|
||||
import Tabs from "@mui/material/Tabs";
|
||||
import Tab from "@mui/material/Tab";
|
||||
|
||||
enum Page {
|
||||
Management,
|
||||
Equipment,
|
||||
Territory,
|
||||
}
|
||||
|
||||
export function GangRoot(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const router = use.Router();
|
||||
const gang = (function () {
|
||||
if (player.gang === null) throw new Error("Gang should not be null");
|
||||
return player.gang;
|
||||
})();
|
||||
const [management, setManagement] = useState(true);
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
function handleChange(event: React.SyntheticEvent, tab: number): void {
|
||||
setValue(tab);
|
||||
}
|
||||
|
||||
const setRerender = useState(false)[1];
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
||||
const id = setInterval(() => setRerender((old) => !old), 200);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
function back(): void {
|
||||
router.toFaction(Factions[gang.facName]);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="gang-container">
|
||||
<a className="a-link-button" style={{ display: "inline-block" }} onClick={back}>
|
||||
Back
|
||||
</a>
|
||||
<a
|
||||
className={management ? "a-link-button-inactive" : "a-link-button"}
|
||||
style={{ display: "inline-block" }}
|
||||
onClick={() => setManagement(true)}
|
||||
>
|
||||
Gang Management
|
||||
</a>
|
||||
<a
|
||||
className={!management ? "a-link-button-inactive" : "a-link-button"}
|
||||
style={{ display: "inline-block" }}
|
||||
onClick={() => setManagement(false)}
|
||||
>
|
||||
Gang Territory
|
||||
</a>
|
||||
{management ? <ManagementSubpage gang={gang} player={player} /> : <TerritorySubpage gang={gang} />}
|
||||
</div>
|
||||
<Context.Gang.Provider value={gang}>
|
||||
<Tabs variant="fullWidth" value={value} onChange={handleChange}>
|
||||
<Tab label="Management" />
|
||||
<Tab label="Equipment" />
|
||||
<Tab label="Territory" />
|
||||
</Tabs>
|
||||
{value === 0 && <ManagementSubpage />}
|
||||
{value === 1 && <EquipmentsSubpage />}
|
||||
{value === 2 && <TerritorySubpage />}
|
||||
</Context.Gang.Provider>
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
*/
|
||||
import React from "react";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import { Gang } from "../Gang";
|
||||
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
@ -12,13 +11,14 @@ import { MoneyRate } from "../../ui/React/MoneyRate";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
import { AllGangs } from "../AllGangs";
|
||||
import { BonusTime } from "./BonusTime";
|
||||
import { useGang } from "./Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
interface IProps {
|
||||
gang: Gang;
|
||||
}
|
||||
|
||||
export function GangStats(props: IProps): React.ReactElement {
|
||||
const territoryMult = AllGangs[props.gang.facName].territory * 100;
|
||||
export function GangStats(): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const territoryMult = AllGangs[gang.facName].territory * 100;
|
||||
let territoryStr;
|
||||
if (territoryMult <= 0) {
|
||||
territoryStr = formatNumber(0, 2);
|
||||
@ -30,46 +30,59 @@ export function GangStats(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Respect: {numeralWrapper.formatRespect(props.gang.respect)} (
|
||||
{numeralWrapper.formatRespect(5 * props.gang.respectGainRate)} / sec)
|
||||
<span className="tooltiptext">
|
||||
Represents the amount of respect your gang has from other gangs and criminal organizations. Your respect
|
||||
affects the amount of money your gang members will earn, and also determines how much reputation you are
|
||||
earning with your gang's corresponding Faction.
|
||||
</span>
|
||||
</p>
|
||||
<br />
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Wanted Level: {numeralWrapper.formatWanted(props.gang.wanted)} (
|
||||
{numeralWrapper.formatWanted(5 * props.gang.wantedGainRate)} / sec)
|
||||
<span className="tooltiptext">
|
||||
Represents how much the gang is wanted by law enforcement. The higher your gang's wanted level, the harder it
|
||||
will be for your gang members to make money and earn respect. Note that the minimum wanted level is 1.
|
||||
</span>
|
||||
</p>
|
||||
<br />
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Wanted Level Penalty: -{formatNumber((1 - props.gang.getWantedPenalty()) * 100, 2)}%
|
||||
<span className="tooltiptext">Penalty for respect and money gain rates due to Wanted Level</span>
|
||||
</p>
|
||||
<br />
|
||||
<div>
|
||||
<p style={{ display: "inline-block" }}>
|
||||
Money gain rate: <MoneyRate money={5 * props.gang.moneyGainRate} />
|
||||
</p>
|
||||
</div>
|
||||
<br />
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Territory: {territoryStr}%
|
||||
<span className="tooltiptext">The percentage of total territory your Gang controls</span>
|
||||
</p>
|
||||
<br />
|
||||
<p style={{ display: "inline-block" }}>
|
||||
Faction reputation: <Reputation reputation={Factions[props.gang.facName].playerReputation} />
|
||||
</p>
|
||||
<br />
|
||||
<BonusTime gang={props.gang} />
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Represents the amount of respect your gang has from other gangs and criminal organizations. Your respect
|
||||
affects the amount of money your gang members will earn, and also determines how much reputation you are
|
||||
earning with your gang's corresponding Faction.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Respect: {numeralWrapper.formatRespect(gang.respect)} (
|
||||
{numeralWrapper.formatRespect(5 * gang.respectGainRate)} / sec)
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Represents how much the gang is wanted by law enforcement. The higher your gang's wanted level, the harder
|
||||
it will be for your gang members to make money and earn respect. Note that the minimum wanted level is 1.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Wanted Level: {numeralWrapper.formatWanted(gang.wanted)} (
|
||||
{numeralWrapper.formatWanted(5 * gang.wantedGainRate)} / sec)
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
||||
<Box display="flex">
|
||||
<Tooltip title={<Typography>Penalty for respect and money gain rates due to Wanted Level</Typography>}>
|
||||
<Typography>Wanted Level Penalty: -{formatNumber((1 - gang.getWantedPenalty()) * 100, 2)}%</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
||||
<Typography>
|
||||
Money gain rate: <MoneyRate money={5 * gang.moneyGainRate} />
|
||||
</Typography>
|
||||
|
||||
<Box display="flex">
|
||||
<Tooltip title={<Typography>The percentage of total territory your Gang controls</Typography>}>
|
||||
<Typography>Territory: {territoryStr}%</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Typography>
|
||||
Faction reputation: <Reputation reputation={Factions[gang.facName].playerReputation} />
|
||||
</Typography>
|
||||
|
||||
<BonusTime gang={gang} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -2,20 +2,16 @@
|
||||
* React Component for the subpage that manages gang members, the main page.
|
||||
*/
|
||||
import React from "react";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { GangStats } from "./GangStats";
|
||||
import { Gang } from "../Gang";
|
||||
import { GangMemberList } from "./GangMemberList";
|
||||
import { useGang } from "./Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
gang: Gang;
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
export function ManagementSubpage(props: IProps): React.ReactElement {
|
||||
export function ManagementSubpage(): React.ReactElement {
|
||||
const gang = useGang();
|
||||
return (
|
||||
<div style={{ display: "block" }}>
|
||||
<p className="noselect" style={{ width: "70%" }}>
|
||||
<>
|
||||
<Typography>
|
||||
This page is used to manage your gang members and get an overview of your gang's stats.
|
||||
<br />
|
||||
<br />
|
||||
@ -23,7 +19,7 @@ export function ManagementSubpage(props: IProps): React.ReactElement {
|
||||
too difficult. Consider training that member's stats or choosing an easier task. The tasks closer to the top of
|
||||
the dropdown list are generally easier. Alternatively, the gang member's low production might be due to the fact
|
||||
that your wanted level is too high. Consider assigning a few members to the '
|
||||
{props.gang.isHackingGang ? "Ethical Hacking" : "Vigilante Justice"}' task to lower your wanted level.
|
||||
{gang.isHackingGang ? "Ethical Hacking" : "Vigilante Justice"}' task to lower your wanted level.
|
||||
<br />
|
||||
<br />
|
||||
Installing Augmentations does NOT reset your progress with your Gang. Furthermore, after installing
|
||||
@ -31,11 +27,11 @@ export function ManagementSubpage(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
<br />
|
||||
You can also manage your gang programmatically through Netscript using the Gang API
|
||||
</p>
|
||||
</Typography>
|
||||
<br />
|
||||
<GangStats gang={props.gang} />
|
||||
<GangStats />
|
||||
<br />
|
||||
<GangMemberList gang={props.gang} player={props.player} />
|
||||
</div>
|
||||
<GangMemberList />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,51 +1,42 @@
|
||||
/**
|
||||
* React Component for the recruitment button and text on the gang main page.
|
||||
*/
|
||||
import React from "react";
|
||||
import { Gang } from "../Gang";
|
||||
import { RecruitPopup } from "./RecruitPopup";
|
||||
import React, { useState } from "react";
|
||||
import { RecruitModal } from "./RecruitModal";
|
||||
import { GangConstants } from "../data/Constants";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { useGang } from "./Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
interface IProps {
|
||||
gang: Gang;
|
||||
onRecruit: () => void;
|
||||
}
|
||||
|
||||
export function RecruitButton(props: IProps): React.ReactElement {
|
||||
if (props.gang.members.length >= GangConstants.MaximumGangMembers) {
|
||||
const gang = useGang();
|
||||
const [open, setOpen] = useState(false);
|
||||
if (gang.members.length >= GangConstants.MaximumGangMembers) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (!props.gang.canRecruitMember()) {
|
||||
const respect = props.gang.getRespectNeededToRecruitMember();
|
||||
if (!gang.canRecruitMember()) {
|
||||
const respect = gang.getRespectNeededToRecruitMember();
|
||||
return (
|
||||
<>
|
||||
<a className="a-link-button-inactive" style={{ display: "inline-block", margin: "10px" }}>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Button sx={{ mx: 1 }} disabled>
|
||||
Recruit Gang Member
|
||||
</a>
|
||||
<p style={{ margin: "10px", color: "red", display: "inline-block" }}>
|
||||
{formatNumber(respect, 2)} respect needed to recruit next member
|
||||
</p>
|
||||
</>
|
||||
</Button>
|
||||
<Typography>{formatNumber(respect, 2)} respect needed to recruit next member</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function onClick(): void {
|
||||
const popupId = "recruit-gang-member-popup";
|
||||
createPopup(popupId, RecruitPopup, {
|
||||
gang: props.gang,
|
||||
popupId: popupId,
|
||||
onRecruit: props.onRecruit,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<a className="a-link-button" onClick={onClick} style={{ display: "inline-block", margin: "10px" }}>
|
||||
Recruit Gang Member
|
||||
</a>
|
||||
<Button onClick={() => setOpen(true)}>Recruit Gang Member</Button>
|
||||
<RecruitModal open={open} onClose={() => setOpen(false)} onRecruit={props.onRecruit} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -2,38 +2,35 @@
|
||||
* React Component for the popup used to recruit new gang members.
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { Gang } from "../Gang";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { useGang } from "./Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
interface IRecruitPopupProps {
|
||||
gang: Gang;
|
||||
popupId: string;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onRecruit: () => void;
|
||||
}
|
||||
|
||||
export function RecruitPopup(props: IRecruitPopupProps): React.ReactElement {
|
||||
export function RecruitModal(props: IRecruitPopupProps): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const [name, setName] = useState("");
|
||||
|
||||
const disabled = name === "" || !gang.canRecruitMember();
|
||||
function recruit(): void {
|
||||
if (name === "") {
|
||||
dialogBoxCreate("You must enter a name for your Gang member!");
|
||||
return;
|
||||
}
|
||||
if (!props.gang.canRecruitMember()) {
|
||||
dialogBoxCreate("You cannot recruit another Gang member!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (disabled) return;
|
||||
// At this point, the only way this can fail is if you already
|
||||
// have a gang member with the same name
|
||||
if (!props.gang.recruitMember(name)) {
|
||||
if (!gang.recruitMember(name)) {
|
||||
dialogBoxCreate("You already have a gang member with this name!");
|
||||
return;
|
||||
}
|
||||
|
||||
props.onRecruit();
|
||||
removePopup(props.popupId);
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
@ -45,20 +42,23 @@ export function RecruitPopup(props: IRecruitPopupProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="noselect">Enter a name for your new Gang member:</p>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography>Enter a name for your new Gang member:</Typography>
|
||||
<br />
|
||||
<input
|
||||
<TextField
|
||||
autoFocus
|
||||
onKeyUp={onKeyUp}
|
||||
onChange={onChange}
|
||||
className="text-input noselect"
|
||||
type="text"
|
||||
placeholder="unique name"
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<Button disabled={disabled} onClick={recruit}>
|
||||
Recruit
|
||||
</Button>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<a className="std-button" onClick={recruit}>
|
||||
Recruit Gang Member
|
||||
</a>
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
import React from "react";
|
||||
import { GangMemberTasks } from "../GangMemberTasks";
|
||||
import { GangMember } from "../GangMember";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
member: GangMember;
|
||||
@ -14,5 +15,5 @@ export function TaskDescription(props: IProps): React.ReactElement {
|
||||
const task = GangMemberTasks[props.member.task];
|
||||
const desc = task ? task.desc : GangMemberTasks["Unassigned"].desc;
|
||||
|
||||
return <p className="inline noselect" dangerouslySetInnerHTML={{ __html: desc }} />;
|
||||
return <Typography dangerouslySetInnerHTML={{ __html: desc }} />;
|
||||
}
|
||||
|
@ -6,49 +6,50 @@ import React, { useState } from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { StatsTable } from "../../ui/React/StatsTable";
|
||||
import { MoneyRate } from "../../ui/React/MoneyRate";
|
||||
import { Gang } from "../Gang";
|
||||
import { useGang } from "./Context";
|
||||
import { GangMember } from "../GangMember";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
|
||||
interface IProps {
|
||||
member: GangMember;
|
||||
gang: Gang;
|
||||
onTaskChange: () => void;
|
||||
}
|
||||
|
||||
export function TaskSelector(props: IProps): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const [currentTask, setCurrentTask] = useState(props.member.task);
|
||||
|
||||
function onChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
function onChange(event: SelectChangeEvent<string>): void {
|
||||
const task = event.target.value;
|
||||
props.member.assignToTask(task);
|
||||
setCurrentTask(task);
|
||||
props.onTaskChange();
|
||||
}
|
||||
|
||||
const tasks = props.gang.getAllTaskNames();
|
||||
const tasks = gang.getAllTaskNames();
|
||||
|
||||
const data = [
|
||||
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(props.gang)} />],
|
||||
[`Respect:`, `${numeralWrapper.formatRespect(5 * props.member.calculateRespectGain(props.gang))} / sec`],
|
||||
[`Wanted Level:`, `${numeralWrapper.formatWanted(5 * props.member.calculateWantedLevelGain(props.gang))} / sec`],
|
||||
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(gang)} />],
|
||||
[`Respect:`, `${numeralWrapper.formatRespect(5 * props.member.calculateRespectGain(gang))} / sec`],
|
||||
[`Wanted Level:`, `${numeralWrapper.formatWanted(5 * props.member.calculateWantedLevelGain(gang))} / sec`],
|
||||
[`Total Respect:`, `${numeralWrapper.formatRespect(props.member.earnedRespect)}`],
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<select onChange={onChange} className="dropdown noselect" value={currentTask}>
|
||||
<option key={0} value={"---"}>
|
||||
---
|
||||
</option>
|
||||
<Select onChange={onChange} value={currentTask}>
|
||||
<MenuItem key={0} value={"Unassigned"}>
|
||||
Unassigned
|
||||
</MenuItem>
|
||||
{tasks.map((task: string, i: number) => (
|
||||
<option key={i + 1} value={task}>
|
||||
<MenuItem key={i + 1} value={task}>
|
||||
{task}
|
||||
</option>
|
||||
</MenuItem>
|
||||
))}
|
||||
</select>
|
||||
<div>
|
||||
<StatsTable rows={data} />
|
||||
</div>
|
||||
</Select>
|
||||
|
||||
<StatsTable rows={data} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -3,59 +3,25 @@
|
||||
*/
|
||||
import React from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { AllGangs } from "../AllGangs";
|
||||
import { Gang } from "../Gang";
|
||||
import { useGang } from "./Context";
|
||||
|
||||
interface IProps {
|
||||
gang: Gang;
|
||||
}
|
||||
import Typography from "@mui/material/Typography";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import Switch from "@mui/material/Switch";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
export function TerritorySubpage(props: IProps): React.ReactElement {
|
||||
function openWarfareHelp(): void {
|
||||
dialogBoxCreate(
|
||||
"This percentage represents the chance you have of " +
|
||||
"'clashing' with with another gang. If you do not " +
|
||||
"wish to gain/lose territory, then keep this " +
|
||||
"percentage at 0% by not engaging in territory warfare.",
|
||||
);
|
||||
}
|
||||
|
||||
function formatTerritory(n: number): string {
|
||||
const v = n * 100;
|
||||
if (v <= 0) {
|
||||
return formatNumber(0, 2);
|
||||
} else if (v >= 100) {
|
||||
return formatNumber(100, 2);
|
||||
} else {
|
||||
return formatNumber(v, 2);
|
||||
}
|
||||
}
|
||||
|
||||
const playerPower = AllGangs[props.gang.facName].power;
|
||||
function otherGangTerritory(name: string): React.ReactElement {
|
||||
const power = AllGangs[name].power;
|
||||
const clashVictoryChance = playerPower / (power + playerPower);
|
||||
return (
|
||||
<span key={name}>
|
||||
<u>{name}</u>
|
||||
<br />
|
||||
Power: {formatNumber(power, 6)}
|
||||
<br />
|
||||
Territory: {formatTerritory(AllGangs[name].territory)}%<br />
|
||||
Chance to win clash with this gang: {numeralWrapper.formatPercentage(clashVictoryChance, 3)}
|
||||
<br />
|
||||
<br />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const gangNames = Object.keys(AllGangs).filter((g) => g != props.gang.facName);
|
||||
export function TerritorySubpage(): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const gangNames = Object.keys(AllGangs).filter((g) => g != gang.facName);
|
||||
|
||||
return (
|
||||
<div style={{ width: "70%" }}>
|
||||
<p className="noselect">
|
||||
<>
|
||||
<Typography>
|
||||
This page shows how much territory your Gang controls. This statistic is listed as a percentage, which
|
||||
represents how much of the total territory you control.
|
||||
<br />
|
||||
@ -76,59 +42,113 @@ export function TerritorySubpage(props: IProps): React.ReactElement {
|
||||
and wanted level. It is very beneficial to have high territory control.
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
<input
|
||||
checked={props.gang.territoryWarfareEngaged}
|
||||
id="warfare"
|
||||
type="checkbox"
|
||||
style={{ display: "inline-block", margin: "2px" }}
|
||||
onChange={(event) => (props.gang.territoryWarfareEngaged = event.target.checked)}
|
||||
</Typography>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={gang.territoryWarfareEngaged}
|
||||
onChange={(event) => (gang.territoryWarfareEngaged = event.target.checked)}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Engaging in Territory Warfare sets your clash chance to 100%. Disengaging will cause your clash chance
|
||||
to gradually decrease until it reaches 0%.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Engage in Territory Warfare</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<label htmlFor="warfare" className="tooltip noselect" style={{ color: "white", display: "inline-block" }}>
|
||||
Engage in Territory Warfare
|
||||
<span className="tooltiptext" style={{ display: "inline-block" }}>
|
||||
Engaging in Territory Warfare sets your clash chance to 100%. Disengaging will cause your clash chance to
|
||||
gradually decrease until it reaches 0%.
|
||||
</span>
|
||||
</label>
|
||||
<br />
|
||||
<p style={{ display: "inline-block" }}>
|
||||
Territory Clash Chance: {numeralWrapper.formatPercentage(props.gang.territoryClashChance, 3)}
|
||||
</p>
|
||||
<div className="help-tip noselect" style={{ display: "inline-block" }} onClick={openWarfareHelp}>
|
||||
?
|
||||
</div>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
This percentage represents the chance you have of 'clashing' with with another gang. If you do not wish to
|
||||
gain/lose territory, then keep this percentage at 0% by not engaging in territory warfare.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Territory Clash Chance: {numeralWrapper.formatPercentage(gang.territoryClashChance, 3)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
|
||||
<input
|
||||
checked={props.gang.notifyMemberDeath}
|
||||
id="notify"
|
||||
type="checkbox"
|
||||
style={{ display: "inline-block", margin: "2px" }}
|
||||
onChange={(event) => (props.gang.notifyMemberDeath = event.target.checked)}
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={gang.notifyMemberDeath}
|
||||
onChange={(event) => (gang.notifyMemberDeath = event.target.checked)}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is enabled, then you will receive a pop-up notifying you whenever one of your Gang Members dies
|
||||
in a territory clash.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Notify about Gang Member Deaths</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<label htmlFor="warfare" className="tooltip noselect" style={{ color: "white", display: "inline-block" }}>
|
||||
Notify about Gang Member Deaths
|
||||
<span className="tooltiptext" style={{ display: "inline-block" }}>
|
||||
If this is enabled, then you will receive a pop-up notifying you whenever one of your Gang Members dies in a
|
||||
territory clash.
|
||||
</span>
|
||||
</label>
|
||||
<br />
|
||||
<fieldset style={{ display: "block", margin: "6px" }}>
|
||||
<p>
|
||||
<Paper>
|
||||
<Typography>
|
||||
<b>
|
||||
<u>{props.gang.facName}</u>
|
||||
<u>{gang.facName}</u>
|
||||
</b>
|
||||
<br />
|
||||
Power: {formatNumber(AllGangs[props.gang.facName].power, 6)}
|
||||
Power: {formatNumber(AllGangs[gang.facName].power, 6)}
|
||||
<br />
|
||||
Territory: {formatTerritory(AllGangs[props.gang.facName].territory)}%
|
||||
Territory: {formatTerritory(AllGangs[gang.facName].territory)}%
|
||||
<br />
|
||||
<br />
|
||||
{gangNames.map((name) => otherGangTerritory(name))}
|
||||
</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
</Typography>
|
||||
{gangNames.map((name) => (
|
||||
<OtherGangTerritory key={name} name={name} />
|
||||
))}
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
function formatTerritory(n: number): string {
|
||||
const v = n * 100;
|
||||
if (v <= 0) {
|
||||
return formatNumber(0, 2);
|
||||
} else if (v >= 100) {
|
||||
return formatNumber(100, 2);
|
||||
} else {
|
||||
return formatNumber(v, 2);
|
||||
}
|
||||
}
|
||||
|
||||
interface ITerritoryProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
function OtherGangTerritory(props: ITerritoryProps): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const playerPower = AllGangs[gang.facName].power;
|
||||
const power = AllGangs[props.name].power;
|
||||
const clashVictoryChance = playerPower / (power + playerPower);
|
||||
return (
|
||||
<Typography>
|
||||
<u>{props.name}</u>
|
||||
<br />
|
||||
Power: {formatNumber(power, 6)}
|
||||
<br />
|
||||
Territory: {formatTerritory(AllGangs[props.name].territory)}%<br />
|
||||
Chance to win clash with this gang: {numeralWrapper.formatPercentage(clashVictoryChance, 3)}
|
||||
<br />
|
||||
<br />
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import { purchaseServer } from "../../Server/ServerPurchases";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import { use } from "../../ui/Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
@ -1,84 +0,0 @@
|
||||
/**
|
||||
* React component to create an accordion element
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
type IProps = {
|
||||
headerClass?: string; // Override default class
|
||||
headerContent: React.ReactElement;
|
||||
panelClass?: string; // Override default class
|
||||
panelContent: React.ReactElement;
|
||||
panelInitiallyOpened?: boolean;
|
||||
style?: string;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
panelOpened: boolean;
|
||||
};
|
||||
|
||||
export class BBAccordion extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.handleHeaderClick = this.handleHeaderClick.bind(this);
|
||||
|
||||
this.state = {
|
||||
panelOpened: props.panelInitiallyOpened ? props.panelInitiallyOpened : false,
|
||||
};
|
||||
}
|
||||
|
||||
handleHeaderClick(): void {
|
||||
this.setState({
|
||||
panelOpened: !this.state.panelOpened,
|
||||
});
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
let className = "accordion-header";
|
||||
if (typeof this.props.headerClass === "string") {
|
||||
className = this.props.headerClass;
|
||||
}
|
||||
|
||||
if (this.state.panelOpened) className += " active";
|
||||
|
||||
return (
|
||||
<>
|
||||
<button className={className} onClick={this.handleHeaderClick}>
|
||||
{this.props.headerContent}
|
||||
</button>
|
||||
<AccordionPanel
|
||||
opened={this.state.panelOpened}
|
||||
panelClass={this.props.panelClass}
|
||||
panelContent={this.props.panelContent}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
type IPanelProps = {
|
||||
opened: boolean;
|
||||
panelClass?: string; // Override default class
|
||||
panelContent: React.ReactElement;
|
||||
};
|
||||
|
||||
class AccordionPanel extends React.Component<IPanelProps, any> {
|
||||
shouldComponentUpdate(nextProps: IPanelProps): boolean {
|
||||
return this.props.opened || nextProps.opened;
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
let className = "accordion-panel";
|
||||
if (typeof this.props.panelClass === "string") {
|
||||
className = this.props.panelClass;
|
||||
}
|
||||
|
||||
if (!this.props.opened) return <></>;
|
||||
|
||||
return (
|
||||
<div className={className} style={{ display: "block" }}>
|
||||
{this.props.panelContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/**
|
||||
* Wrapper around material-ui's Button component that styles it with
|
||||
* Bitburner's UI theme
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Button, ButtonProps } from "@mui/material";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
const useStyles = makeStyles({
|
||||
// Tries to emulate StdButton in buttons.scss
|
||||
root: {
|
||||
backgroundColor: "#555",
|
||||
border: "1px solid #333",
|
||||
color: "white",
|
||||
margin: "5px",
|
||||
padding: "3px 5px",
|
||||
"&:hover": {
|
||||
backgroundColor: "#666",
|
||||
},
|
||||
|
||||
borderRadius: 0,
|
||||
},
|
||||
textPrimary: {
|
||||
color: "rgb( 144, 202, 249)",
|
||||
},
|
||||
textSecondary: {
|
||||
color: "rgb(244, 143, 177)",
|
||||
},
|
||||
disabled: {
|
||||
backgroundColor: "#333",
|
||||
color: "#fff",
|
||||
cursor: "default",
|
||||
},
|
||||
});
|
||||
|
||||
export const MuiButton: React.FC<ButtonProps> = (props: ButtonProps) => {
|
||||
return (
|
||||
<Button
|
||||
{...props}
|
||||
classes={{
|
||||
...useStyles(),
|
||||
...props.classes,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Wrapper around material-ui's Button component that styles it with
|
||||
* Bitburner's UI theme
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Paper, PaperProps } from "@mui/material";
|
||||
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
backgroundColor: "rgb(30, 30, 30)",
|
||||
border: "2px solid #000",
|
||||
borderRadius: "10px",
|
||||
display: "inline-block",
|
||||
flexWrap: "wrap",
|
||||
padding: "10px",
|
||||
},
|
||||
});
|
||||
|
||||
export const MuiPaper: React.FC<PaperProps> = (props: PaperProps) => {
|
||||
return (
|
||||
<Paper
|
||||
{...props}
|
||||
classes={{
|
||||
...useStyles(),
|
||||
...props.classes,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -16,7 +16,7 @@ export function Reputation({ reputation }: { reputation: number | string }): Rea
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<span className={classes.reputation}>
|
||||
{typeof reputation === "number" ? numeralWrapper.formatFavor(reputation) : reputation}
|
||||
{typeof reputation === "number" ? numeralWrapper.formatReputation(reputation) : reputation}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Basic stateless button
|
||||
* Uses the 'std-button' css class
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
interface IStdButtonProps {
|
||||
addClasses?: string;
|
||||
autoFocus?: boolean;
|
||||
disabled?: boolean;
|
||||
id?: string;
|
||||
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
|
||||
onKeyUp?: (e: React.KeyboardEvent<HTMLElement>) => any;
|
||||
style?: any;
|
||||
text: string | JSX.Element;
|
||||
tooltip?: string | JSX.Element;
|
||||
}
|
||||
|
||||
type IInnerHTMLMarkup = {
|
||||
__html: string;
|
||||
};
|
||||
|
||||
export function StdButton(props: IStdButtonProps): React.ReactElement {
|
||||
const hasTooltip = props.tooltip != null && props.tooltip !== "";
|
||||
let className = props.disabled ? "std-button-disabled" : "std-button";
|
||||
if (hasTooltip) {
|
||||
className += " tooltip";
|
||||
}
|
||||
|
||||
if (typeof props.addClasses === "string") {
|
||||
className += ` ${props.addClasses}`;
|
||||
}
|
||||
|
||||
// Tooltip will be set using inner HTML
|
||||
let tooltip;
|
||||
if (hasTooltip) {
|
||||
if (typeof props.tooltip === "string") {
|
||||
const tooltipMarkup: IInnerHTMLMarkup = {
|
||||
__html: props.tooltip,
|
||||
};
|
||||
tooltip = <span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup}></span>;
|
||||
} else {
|
||||
tooltip = <span className={"tooltiptext"}>{props.tooltip}</span>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={className}
|
||||
id={props.id}
|
||||
onClick={props.onClick}
|
||||
onKeyUp={props.onKeyUp}
|
||||
style={props.style}
|
||||
autoFocus={props.autoFocus}
|
||||
>
|
||||
{props.text}
|
||||
{hasTooltip && tooltip}
|
||||
</button>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user