Merge pull request #1396 from danielyxie/dev

Gang in mui
This commit is contained in:
hydroflame 2021-10-01 15:40:15 -04:00 committed by GitHub
commit 7785801764
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 699 additions and 2713 deletions

66
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -6,13 +6,12 @@ import { enterBitNode, setRedPillFlag } from "../../RedPill";
import { PortalModal } from "./PortalModal"; import { PortalModal } from "./PortalModal";
import { CinematicText } from "../../ui/React/CinematicText"; import { CinematicText } from "../../ui/React/CinematicText";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { Theme } from "@mui/material";
import makeStyles from "@mui/styles/makeStyles"; import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles(() =>
createStyles({ createStyles({
level0: { level0: {
color: "red", color: "red",

@ -1,4 +1,3 @@
import React from "react";
import { Player } from "../Player"; import { Player } from "../Player";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { addOffset } from "../utils/helpers/addOffset"; import { addOffset } from "../utils/helpers/addOffset";

@ -1,4 +1,3 @@
import React from "react";
import { BlackOperation } from "./BlackOperation"; import { BlackOperation } from "./BlackOperation";
import { IMap } from "../types"; import { IMap } from "../types";

@ -1,4 +1,3 @@
import React from "react";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
import { IBladeburner } from "./IBladeburner"; import { IBladeburner } from "./IBladeburner";
import { IActionIdentifier } from "./IActionIdentifier"; import { IActionIdentifier } from "./IActionIdentifier";

@ -1,4 +1,3 @@
import React from "react";
import { Action } from "./Action"; import { Action } from "./Action";
import { IMap } from "../types"; import { IMap } from "../types";

@ -10,7 +10,6 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import Tabs from "@mui/material/Tabs"; import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab"; import Tab from "@mui/material/Tab";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;

@ -37,9 +37,7 @@ export function SkillElem(props: IProps): React.ReactElement {
return ( return (
<Paper sx={{ my: 1, p: 1 }}> <Paper sx={{ my: 1, p: 1 }}>
<Box display="flex" flexDirection="row" alignItems="center"> <Box display="flex" flexDirection="row" alignItems="center">
<Typography> <CopyableText variant="h6" color="primary" value={props.skill.name} />
<CopyableText value={props.skill.name} />
</Typography>
{!canLevel || maxLvl ? ( {!canLevel || maxLvl ? (
<IconButton disabled> <IconButton disabled>
<CloseIcon /> <CloseIcon />
@ -50,8 +48,6 @@ export function SkillElem(props: IProps): React.ReactElement {
</IconButton> </IconButton>
)} )}
</Box> </Box>
<br />
<br />
<Typography>Level: {currentLevel}</Typography> <Typography>Level: {currentLevel}</Typography>
{maxLvl ? ( {maxLvl ? (
<Typography>MAX LEVEL</Typography> <Typography>MAX LEVEL</Typography>

@ -26,12 +26,9 @@ export function SkillPage(props: IProps): React.ReactElement {
You will gain one skill point every{" "} You will gain one skill point every{" "}
{BladeburnerConstants.RanksPerSkillPoint * BitNodeMultipliers.BladeburnerSkillCost} ranks. {BladeburnerConstants.RanksPerSkillPoint * BitNodeMultipliers.BladeburnerSkillCost} ranks.
<br /> <br />
<br />
Note that when upgrading a skill, the benefit for that skill is additive. However, the effects of different Note that when upgrading a skill, the benefit for that skill is additive. However, the effects of different
skills with each other is multiplicative. skills with each other is multiplicative.
<br />
</Typography> </Typography>
<br />
{valid(mults["successChanceAll"]) && ( {valid(mults["successChanceAll"]) && (
<Typography>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}</Typography> <Typography>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}</Typography>
)} )}
@ -61,7 +58,6 @@ export function SkillPage(props: IProps): React.ReactElement {
{valid(mults["stamina"]) && <Typography>Stamina: x{formatNumber(mults["stamina"], 3)}</Typography>} {valid(mults["stamina"]) && <Typography>Stamina: x{formatNumber(mults["stamina"], 3)}</Typography>}
{valid(mults["money"]) && <Typography>Contract Money: x{formatNumber(mults["money"], 3)}</Typography>} {valid(mults["money"]) && <Typography>Contract Money: x{formatNumber(mults["money"], 3)}</Typography>}
{valid(mults["expGain"]) && <Typography>Exp Gain: x{formatNumber(mults["expGain"], 3)}</Typography>} {valid(mults["expGain"]) && <Typography>Exp Gain: x{formatNumber(mults["expGain"], 3)}</Typography>}
<br />
<SkillList bladeburner={props.bladeburner} onUpgrade={() => setRerender((old) => !old)} /> <SkillList bladeburner={props.bladeburner} onUpgrade={() => setRerender((old) => !old)} />
</> </>
); );

@ -3,7 +3,6 @@ import { Card, Suit } from "./Card";
import makeStyles from "@mui/styles/makeStyles"; import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
type Props = { type Props = {

@ -13,14 +13,6 @@ type IProps = {
p: IPlayer; p: IPlayer;
}; };
type IState = {
index: number[];
locks: number[];
investment: number;
canPlay: boolean;
status: string | JSX.Element;
};
// statically shuffled array of symbols. // statically shuffled array of symbols.
const symbols = [ const symbols = [
"D", "D",

@ -47,14 +47,6 @@ export const CONSTANTS: {
IntelligenceTerminalHackBaseExpGain: number; IntelligenceTerminalHackBaseExpGain: number;
IntelligenceSingFnBaseExpGain: number; IntelligenceSingFnBaseExpGain: number;
IntelligenceClassBaseExpGain: number; IntelligenceClassBaseExpGain: number;
IntelligenceHackingMissionBaseExpGain: number;
HackingMissionRepToDiffConversion: number;
HackingMissionRepToRewardConversion: number;
HackingMissionSpamTimeIncrease: number;
HackingMissionTransferAttackIncrease: number;
HackingMissionMiscDefenseIncrease: number;
HackingMissionDifficultyToHacking: number;
HackingMissionHowToPlay: string;
MillisecondsPer20Hours: number; MillisecondsPer20Hours: number;
GameCyclesPer20Hours: number; GameCyclesPer20Hours: number;
MillisecondsPer10Hours: number; MillisecondsPer10Hours: number;
@ -199,63 +191,6 @@ export const CONSTANTS: {
IntelligenceTerminalHackBaseExpGain: 200, // Hacking exp divided by this to determine int exp gain IntelligenceTerminalHackBaseExpGain: 200, // Hacking exp divided by this to determine int exp gain
IntelligenceSingFnBaseExpGain: 1.5, IntelligenceSingFnBaseExpGain: 1.5,
IntelligenceClassBaseExpGain: 0.01, IntelligenceClassBaseExpGain: 0.01,
IntelligenceHackingMissionBaseExpGain: 3, // Hacking Mission difficulty multiplied by this to get exp gain
// Hacking Missions
// TODO Move this into Hacking Mission implementation
HackingMissionRepToDiffConversion: 10000, // Faction rep is divided by this to get mission difficulty
HackingMissionRepToRewardConversion: 7, // Faction rep divided byt his to get mission rep reward
HackingMissionSpamTimeIncrease: 25000, // How much time limit increase is gained when conquering a Spam Node (ms)
HackingMissionTransferAttackIncrease: 1.05, // Multiplier by which the attack for all Core Nodes is increased when conquering a Transfer Node
HackingMissionMiscDefenseIncrease: 1.05, // The amount by which every misc node's defense is multiplied when one is conquered
HackingMissionDifficultyToHacking: 135, // Difficulty is multiplied by this to determine enemy's "hacking" level (to determine effects of scan/attack, etc)
HackingMissionHowToPlay:
"Hacking missions are a minigame that, if won, will reward you with faction reputation.<br><br>" +
"In this game you control a set of Nodes and use them to try and defeat an enemy. Your Nodes " +
"are colored blue, while the enemy's are red. There are also other nodes on the map colored gray " +
"that initially belong to neither you nor the enemy. The goal of the game is " +
"to capture all of the enemy's Database nodes within the time limit. " +
"If you fail to do this, you will lose.<br><br>" +
"Each Node has three stats: Attack, Defense, and HP. There are five different actions that " +
"a Node can take:<br><br> " +
"Attack - Targets an enemy Node and lowers its HP. The effectiveness is determined by the owner's Attack, the Player's " +
"hacking level, and the enemy's defense.<br><br>" +
"Scan - Targets an enemy Node and lowers its Defense. The effectiveness is determined by the owner's Attack, the Player's hacking level, and the " +
"enemy's defense.<br><br>" +
"Weaken - Targets an enemy Node and lowers its Attack. The effectiveness is determined by the owner's Attack, the Player's hacking level, and the enemy's " +
"defense.<br><br>" +
"Fortify - Raises the Node's Defense. The effectiveness is determined by your hacking level.<br><br>" +
"Overflow - Raises the Node's Attack but lowers its Defense. The effectiveness is determined by your hacking level.<br><br>" +
"Note that when determining the effectiveness of the above actions, the TOTAL Attack or Defense of the team is used, not just the " +
"Attack/Defense of the individual Node that is performing the action.<br><br>" +
"To capture a Node, you must lower its HP down to 0.<br><br>" +
"There are six different types of Nodes:<br><br>" +
"CPU Core - These are your main Nodes that are used to perform actions. Capable of performing every action<br><br>" +
"Firewall - Nodes with high defense. These Nodes can 'Fortify'<br><br>" +
"Database - A special type of Node. The player's objective is to conquer all of the enemy's Database Nodes within " +
"the time limit. These Nodes cannot perform any actions<br><br>" +
"Spam - Conquering one of these Nodes will slow the enemy's trace, giving the player additional time to complete " +
"the mission. These Nodes cannot perform any actions<br><br>" +
"Transfer - Conquering one of these nodes will increase the Attack of all of your CPU Cores by a small fixed percentage. " +
"These Nodes are capable of performing every action except the 'Attack' action<br><br>" +
"Shield - Nodes with high defense. These Nodes can 'Fortify'<br><br>" +
"To assign an action to a Node, you must first select one of your Nodes. This can be done by simply clicking on it. Double-clicking " +
"a node will select all of your Nodes of the same type (e.g. select all CPU Core Nodes or all Transfer Nodes). Note that only Nodes " +
"that can perform actions (CPU Core, Transfer, Shield, Firewall) can be selected. Selected Nodes will be denoted with a white highlight. After selecting a Node or multiple Nodes, " +
"select its action using the Action Buttons near the top of the screen. Every action also has a corresponding keyboard " +
"shortcut.<br><br>" +
"For certain actions such as attacking, scanning, and weakening, the Node performing the action must have a target. To target " +
"another node, simply click-and-drag from the 'source' Node to a target. A Node can only have one target, and you can target " +
"any Node that is adjacent to one of your Nodes (immediately above, below, or to the side. NOT diagonal). Furthermore, only CPU Cores and Transfer Nodes " +
"can target, since they are the only ones that can perform the related actions. To remove a target, you can simply click on the line that represents " +
"the connection between one of your Nodes and its target. Alternatively, you can select the 'source' Node and click the 'Drop Connection' button, " +
"or press 'd'.<br><br>" +
"Other Notes:<br><br>" +
"-Whenever a miscellenaous Node (not owned by the player or enemy) is conquered, the defense of all remaining miscellaneous Nodes that " +
"are not actively being targeted will increase by a fixed percentage.<br><br>" +
"-Whenever a Node is conquered, its stats are significantly reduced<br><br>" +
"-Miscellaneous Nodes slowly raise their defense over time<br><br>" +
"-Nodes slowly regenerate health over time.",
// Time-related constants // Time-related constants
MillisecondsPer20Hours: 72000000, MillisecondsPer20Hours: 72000000,

@ -1,11 +1,8 @@
import { CorporationConstants } from "./data/Constants"; import { CorporationConstants } from "./data/Constants";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import { createElement } from "../ui/uiHelpers/createElement";
import { EmployeePositions } from "./EmployeePositions"; import { EmployeePositions } from "./EmployeePositions";
import { ICorporation } from "./ICorporation"; import { ICorporation } from "./ICorporation";
import { numeralWrapper } from "../ui/numeralFormat";
import { formatNumber } from "../utils/StringHelperFunctions";
import { OfficeSpace } from "./OfficeSpace"; import { OfficeSpace } from "./OfficeSpace";
import { IIndustry } from "./IIndustry"; import { IIndustry } from "./IIndustry";

@ -16,7 +16,6 @@ import { Warehouse } from "./Warehouse";
import { ICorporation } from "./ICorporation"; import { ICorporation } from "./ICorporation";
import { IIndustry } from "./IIndustry"; import { IIndustry } from "./IIndustry";
import { IndustryUpgrade, IndustryUpgrades } from "./IndustryUpgrades"; import { IndustryUpgrade, IndustryUpgrades } from "./IndustryUpgrades";
import { formatNumber } from "../utils/StringHelperFunctions";
interface IParams { interface IParams {
name?: string; name?: string;

@ -3,7 +3,6 @@ import { ICorporation } from "./ICorporation";
import { IIndustry } from "./IIndustry"; import { IIndustry } from "./IIndustry";
import { MaterialSizes } from "./MaterialSizes"; import { MaterialSizes } from "./MaterialSizes";
import { IMap } from "../types"; import { IMap } from "../types";
import { numeralWrapper } from "../ui/numeralFormat";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";

@ -1,7 +1,6 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { useCorporation } from "./Context"; import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";

@ -10,5 +10,5 @@ export const Context: {
Division: React.createContext<IIndustry>({} as IIndustry), Division: React.createContext<IIndustry>({} as IIndustry),
}; };
export const useCorporation = () => useContext(Context.Corporation); export const useCorporation = (): ICorporation => useContext(Context.Corporation);
export const useDivision = () => useContext(Context.Division); export const useDivision = (): IIndustry => useContext(Context.Division);

@ -5,8 +5,7 @@ import React from "react";
import { CityTabs } from "./CityTabs"; import { CityTabs } from "./CityTabs";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { useCorporation } from "./Context"; import { Context, useCorporation } from "./Context";
import { Context } from "./Context";
import { CityName } from "../../Locations/data/CityNames"; import { CityName } from "../../Locations/data/CityNames";

@ -83,7 +83,6 @@ interface IProps {
// Create a popup that lets the player use the Market TA research for Products // Create a popup that lets the player use the Market TA research for Products
export function ProductMarketTaModal(props: IProps): React.ReactElement { export function ProductMarketTaModal(props: IProps): React.ReactElement {
const division = useDivision();
const markupLimit = props.product.rat / props.product.mku; const markupLimit = props.product.rat / props.product.mku;
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {

@ -6,7 +6,6 @@ import { CorporationConstants } from "../data/Constants";
import { Treant } from "treant-js"; import { Treant } from "treant-js";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { Research } from "../Actions"; import { Research } from "../Actions";
import Typography from "@mui/material/Typography";
interface IProps { interface IProps {
industry: IIndustry; industry: IIndustry;

@ -7,7 +7,6 @@ import { CONSTANTS } from "../Constants";
import { Faction } from "./Faction"; import { Faction } from "./Faction";
import { Factions } from "./Factions"; import { Factions } from "./Factions";
import { HackingMission, setInMission } from "../Missions";
import { Player } from "../Player"; import { Player } from "../Player";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { import {
@ -49,12 +48,6 @@ export function joinFaction(faction: Faction): void {
} }
} }
export function startHackingMission(faction: Faction): void {
const mission = new HackingMission(faction.playerReputation, faction);
setInMission(true, mission); //Sets inMission flag to true
mission.init();
}
//Returns a boolean indicating whether the player has the prerequisites for the //Returns a boolean indicating whether the player has the prerequisites for the
//specified Augmentation //specified Augmentation
export function hasAugmentationPrereqs(aug: Augmentation): boolean { export function hasAugmentationPrereqs(aug: Augmentation): boolean {

@ -13,6 +13,8 @@ import { Settings } from "../../Settings/Settings";
import { hasAugmentationPrereqs } from "../FactionHelpers"; import { hasAugmentationPrereqs } from "../FactionHelpers";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
@ -181,6 +183,9 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
<Typography> <Typography>
These are all of the Augmentations that are available to purchase from {props.faction.name}. Augmentations are These are all of the Augmentations that are available to purchase from {props.faction.name}. Augmentations are
powerful upgrades that will enhance your abilities. powerful upgrades that will enhance your abilities.
<br />
Reputation: <Reputation reputation={props.faction.playerReputation} /> Favor:{" "}
<Favor favor={props.faction.favor} />
</Typography> </Typography>
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button> <Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}>Sort by Reputation</Button> <Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}>Sort by Reputation</Button>

@ -16,7 +16,6 @@ import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { createPopup } from "../../ui/React/createPopup";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { CreateGangModal } from "./CreateGangModal"; import { CreateGangModal } from "./CreateGangModal";
@ -30,10 +29,6 @@ type IProps = {
// Info text for all options on the UI // Info text for all options on the UI
const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and " + "faction reputation"; const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and " + "faction reputation";
const hackingMissionInfo =
"Attempt a hacking mission for your faction. " +
"A mission is a mini game that, if won, earns you " +
"significant reputation with this faction. (Recommended hacking level: 200+)";
const hackingContractsInfo = const hackingContractsInfo =
"Complete hacking contracts for your faction. " + "Complete hacking contracts for your faction. " +
"Your effectiveness, which determines how much " + "Your effectiveness, which determines how much " +
@ -96,11 +91,6 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
player.startFactionHackWork(router, faction); player.startFactionHackWork(router, faction);
} }
function startHackingMission(faction: Faction): void {
player.singularityStopWork();
router.toHackingMission(faction);
}
function startSecurityWork(faction: Faction): void { function startSecurityWork(faction: Faction): void {
player.startFactionSecurityWork(router, faction); player.startFactionSecurityWork(router, faction);
} }
@ -138,13 +128,6 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
<CreateGangModal facName={faction.name} open={gangOpen} onClose={() => setGangOpen(false)} /> <CreateGangModal facName={faction.name} open={gangOpen} onClose={() => setGangOpen(false)} />
</> </>
)} )}
{!isPlayersGang && factionInfo.offerHackingMission && (
<Option
buttonText={"Hacking Mission"}
infoText={hackingMissionInfo}
onClick={() => startHackingMission(faction)}
/>
)}
{!isPlayersGang && factionInfo.offerHackingWork && ( {!isPlayersGang && factionInfo.offerHackingWork && (
<Option <Option
buttonText={"Hacking Contracts"} buttonText={"Hacking Contracts"}

@ -38,7 +38,7 @@ export interface IGang {
getWantedPenalty(): number; getWantedPenalty(): number;
calculatePower(): number; calculatePower(): number;
killMember(member: GangMember): void; killMember(member: GangMember): void;
ascendMember(member: GangMember, workerScript: WorkerScript): IAscensionResult; ascendMember(member: GangMember, workerScript?: WorkerScript): IAscensionResult;
getDiscount(): number; getDiscount(): number;
getAllTaskNames(): string[]; getAllTaskNames(): string[];
getUpgradeCost(upg: GangMemberUpgrade): number; getUpgradeCost(upg: GangMemberUpgrade): number;

@ -3,20 +3,23 @@
* ascension of a gang member. * ascension of a gang member.
*/ */
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Gang } from "../Gang";
import { GangMember } from "../GangMember"; import { GangMember } from "../GangMember";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { removePopup } from "../../ui/React/createPopup";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; 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 { interface IProps {
open: boolean;
onClose: () => void;
member: GangMember; member: GangMember;
gang: Gang;
popupId: string;
onAscend: () => void; onAscend: () => void;
} }
export function AscensionPopup(props: IProps): React.ReactElement { export function AscensionModal(props: IProps): React.ReactElement {
const gang = useGang();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
useEffect(() => { useEffect(() => {
@ -26,9 +29,9 @@ export function AscensionPopup(props: IProps): React.ReactElement {
function confirm(): void { function confirm(): void {
props.onAscend(); props.onAscend();
const res = props.gang.ascendMember(props.member); const res = gang.ascendMember(props.member);
dialogBoxCreate( dialogBoxCreate(
<p> <Typography>
You ascended {props.member.name}!<br /> You ascended {props.member.name}!<br />
<br /> <br />
Your gang lost {numeralWrapper.formatRespect(res.respect)} respect. Your gang lost {numeralWrapper.formatRespect(res.respect)} respect.
@ -48,13 +51,9 @@ export function AscensionPopup(props: IProps): React.ReactElement {
<br /> <br />
Charisma: x{numeralWrapper.format(res.cha, "0.000")} Charisma: x{numeralWrapper.format(res.cha, "0.000")}
<br /> <br />
</p>, </Typography>,
); );
removePopup(props.popupId); props.onClose();
}
function cancel(): void {
removePopup(props.popupId);
} }
// const ascendBenefits = props.member.getAscensionResults(); // const ascendBenefits = props.member.getAscensionResults();
@ -62,8 +61,8 @@ export function AscensionPopup(props: IProps): React.ReactElement {
const postAscend = props.member.getAscensionMultsAfterAscend(); const postAscend = props.member.getAscensionMultsAfterAscend();
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<pre> <Typography>
Are you sure you want to ascend this member? They will lose all of Are you sure you want to ascend this member? They will lose all of
<br /> <br />
their non-Augmentation upgrades and their stats will reset back to 1. their non-Augmentation upgrades and their stats will reset back to 1.
@ -92,13 +91,8 @@ export function AscensionPopup(props: IProps): React.ReactElement {
Charisma: x{numeralWrapper.format(preAscend.cha, "0.000")} =&gt; x Charisma: x{numeralWrapper.format(preAscend.cha, "0.000")} =&gt; x
{numeralWrapper.format(postAscend.cha, "0.000")} {numeralWrapper.format(postAscend.cha, "0.000")}
<br /> <br />
</pre> </Typography>
<button className="std-button" onClick={confirm}> <Button onClick={confirm}>Ascend</Button>
Ascend </Modal>
</button>
<button className="std-button" onClick={cancel}>
Cancel
</button>
</>
); );
} }

@ -5,6 +5,9 @@ import * as React from "react";
import { Gang } from "../Gang"; import { Gang } from "../Gang";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions"; 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 { interface IProps {
gang: Gang; gang: Gang;
@ -15,15 +18,17 @@ export function BonusTime(props: IProps): React.ReactElement {
if ((props.gang.storedCycles / CyclerPerSecond) * 1000 <= 5000) return <></>; if ((props.gang.storedCycles / CyclerPerSecond) * 1000 <= 5000) return <></>;
const bonusMillis = (props.gang.storedCycles / CyclerPerSecond) * 1000; const bonusMillis = (props.gang.storedCycles / CyclerPerSecond) * 1000;
return ( return (
<> <Box display="flex">
<p className="tooltip" style={{ display: "inline-block" }}> <Tooltip
Bonus time: {convertTimeMsToTimeElapsedString(bonusMillis)} title={
<span className="tooltiptext noselect"> <Typography>
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the 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. browser). Bonus time makes the Gang mechanic progress faster, up to 5x the normal speed.
</span> </Typography>
</p> }
<br /> >
</> <Typography>Bonus time: {convertTimeMsToTimeElapsedString(bonusMillis)}</Typography>
</Tooltip>
</Box>
); );
} }

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 = (): IGang => useContext(Context.Gang);

@ -0,0 +1,219 @@
/**
* 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";
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. * React Component for a gang member on the management subpage.
*/ */
import React from "react"; import React, { useState } from "react";
import { Gang } from "../Gang";
import { GangMember } from "../GangMember"; import { GangMember } from "../GangMember";
import { BBAccordion } from "../../ui/React/BBAccordion";
import { GangMemberAccordionContent } from "./GangMemberAccordionContent"; 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 { interface IProps {
gang: Gang;
member: GangMember; member: GangMember;
} }
export function GangMemberAccordion(props: IProps): React.ReactElement { export function GangMemberAccordion(props: IProps): React.ReactElement {
const [open, setOpen] = useState(true);
return ( return (
<BBAccordion <Box component={Paper}>
panelInitiallyOpened={true} <ListItemButton onClick={() => setOpen((old) => !old)}>
headerContent={<>{props.member.name}</>} <ListItemText primary={<Typography>{props.member.name}</Typography>} />
panelContent={<GangMemberAccordionContent gang={props.gang} member={props.member} />} {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 { GangMemberStats } from "./GangMemberStats";
import { TaskSelector } from "./TaskSelector"; import { TaskSelector } from "./TaskSelector";
import { TaskDescription } from "./TaskDescription"; import { TaskDescription } from "./TaskDescription";
import { Gang } from "../Gang";
import { GangMember } from "../GangMember"; import { GangMember } from "../GangMember";
import Grid from "@mui/material/Grid";
interface IProps { interface IProps {
gang: Gang;
member: GangMember; member: GangMember;
} }
export function GangMemberAccordionContent(props: IProps): React.ReactElement { export function GangMemberAccordionContent(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
return ( return (
<> <Grid container>
<div className={"gang-member-info-div tooltip"}> <Grid item xs={4}>
<GangMemberStats onAscend={() => setRerender((old) => !old)} gang={props.gang} member={props.member} /> <GangMemberStats onAscend={() => setRerender((old) => !old)} member={props.member} />
</div> </Grid>
<div className={"gang-member-info-div"}> <Grid item xs={4}>
<TaskSelector onTaskChange={() => setRerender((old) => !old)} gang={props.gang} member={props.member} /> <TaskSelector onTaskChange={() => setRerender((old) => !old)} member={props.member} />
</div> </Grid>
<div className={"gang-member-info-div"}> <Grid item xs={4}>
<TaskDescription member={props.member} /> <TaskDescription member={props.member} />
</div> </Grid>
</> </Grid>
); );
} }

@ -2,59 +2,21 @@
* React Component for the list of gang members on the management subpage. * React Component for the list of gang members on the management subpage.
*/ */
import React, { useState } from "react"; import React, { useState } from "react";
import { GangMemberUpgradePopup } from "./GangMemberUpgradePopup";
import { GangMemberAccordion } from "./GangMemberAccordion"; import { GangMemberAccordion } from "./GangMemberAccordion";
import { createPopup } from "../../ui/React/createPopup";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Gang } from "../Gang";
import { GangMember } from "../GangMember"; import { GangMember } from "../GangMember";
import { RecruitButton } from "./RecruitButton"; import { RecruitButton } from "./RecruitButton";
import { useGang } from "./Context";
interface IProps { export function GangMemberList(): React.ReactElement {
gang: Gang; const gang = useGang();
player: IPlayer;
}
export function GangMemberList(props: IProps): React.ReactElement {
const [filter, setFilter] = useState("");
const setRerender = useState(false)[1]; 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 ( return (
<> <>
<RecruitButton onRecruit={() => setRerender((old) => !old)} gang={props.gang} /> <RecruitButton onRecruit={() => setRerender((old) => !old)} />
<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>
<ul> <ul>
{members.map((member: GangMember) => ( {gang.members.map((member: GangMember) => (
<li key={member.name}> <GangMemberAccordion key={member.name} member={member} />
<GangMemberAccordion gang={props.gang} member={member} />
</li>
))} ))}
</ul> </ul>
</> </>

@ -2,48 +2,26 @@
* React Component for the first part of a gang member details. * React Component for the first part of a gang member details.
* Contains skills and exp. * Contains skills and exp.
*/ */
import React from "react"; import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { createPopup } from "../../ui/React/createPopup";
import { Gang } from "../Gang";
import { GangMember } from "../GangMember"; 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 { interface IProps {
member: GangMember; member: GangMember;
gang: Gang;
onAscend: () => void; onAscend: () => void;
} }
export function GangMemberStats(props: IProps): React.ReactElement { export function GangMemberStats(props: IProps): React.ReactElement {
function ascend(): void { const [helpOpen, setHelpOpen] = useState(false);
const popupId = `gang-management-ascend-member ${props.member.name}`; const [ascendOpen, setAscendOpen] = useState(false);
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 asc = { const asc = {
hack: props.member.calculateAscensionMult(props.member.hack_asc_points), hack: props.member.calculateAscensionMult(props.member.hack_asc_points),
@ -56,26 +34,36 @@ export function GangMemberStats(props: IProps): React.ReactElement {
return ( return (
<> <>
<span className="tooltiptext smallfont"> <Tooltip
title={
<Typography>
Hk: x{numeralWrapper.formatMultiplier(props.member.hack_mult * asc.hack)}(x Hk: x{numeralWrapper.formatMultiplier(props.member.hack_mult * asc.hack)}(x
{numeralWrapper.formatMultiplier(props.member.hack_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.hack)} Asc) {numeralWrapper.formatMultiplier(props.member.hack_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.hack)}{" "}
Asc)
<br /> <br />
St: x{numeralWrapper.formatMultiplier(props.member.str_mult * asc.str)} St: x{numeralWrapper.formatMultiplier(props.member.str_mult * asc.str)}
(x{numeralWrapper.formatMultiplier(props.member.str_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.str)} Asc) (x{numeralWrapper.formatMultiplier(props.member.str_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.str)}{" "}
Asc)
<br /> <br />
Df: x{numeralWrapper.formatMultiplier(props.member.def_mult * asc.def)} Df: x{numeralWrapper.formatMultiplier(props.member.def_mult * asc.def)}
(x{numeralWrapper.formatMultiplier(props.member.def_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.def)} Asc) (x{numeralWrapper.formatMultiplier(props.member.def_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.def)}{" "}
Asc)
<br /> <br />
Dx: x{numeralWrapper.formatMultiplier(props.member.dex_mult * asc.dex)} Dx: x{numeralWrapper.formatMultiplier(props.member.dex_mult * asc.dex)}
(x{numeralWrapper.formatMultiplier(props.member.dex_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.dex)} Asc) (x{numeralWrapper.formatMultiplier(props.member.dex_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.dex)}{" "}
Asc)
<br /> <br />
Ag: x{numeralWrapper.formatMultiplier(props.member.agi_mult * asc.agi)} Ag: x{numeralWrapper.formatMultiplier(props.member.agi_mult * asc.agi)}
(x{numeralWrapper.formatMultiplier(props.member.agi_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.agi)} Asc) (x{numeralWrapper.formatMultiplier(props.member.agi_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.agi)}{" "}
Asc)
<br /> <br />
Ch: x{numeralWrapper.formatMultiplier(props.member.cha_mult * asc.cha)} Ch: x{numeralWrapper.formatMultiplier(props.member.cha_mult * asc.cha)}
(x{numeralWrapper.formatMultiplier(props.member.cha_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.cha)} Asc) (x{numeralWrapper.formatMultiplier(props.member.cha_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.cha)}{" "}
</span> Asc)
<pre> </Typography>
}
>
<Typography>
Hacking: {formatNumber(props.member.hack, 0)} ({numeralWrapper.formatExp(props.member.hack_exp)} exp) Hacking: {formatNumber(props.member.hack, 0)} ({numeralWrapper.formatExp(props.member.hack_exp)} exp)
<br /> <br />
Strength: {formatNumber(props.member.str, 0)} ({numeralWrapper.formatExp(props.member.str_exp)} exp) Strength: {formatNumber(props.member.str, 0)} ({numeralWrapper.formatExp(props.member.str_exp)} exp)
@ -88,16 +76,35 @@ export function GangMemberStats(props: IProps): React.ReactElement {
<br /> <br />
Charisma: {formatNumber(props.member.cha, 0)} ({numeralWrapper.formatExp(props.member.cha_exp)} exp) Charisma: {formatNumber(props.member.cha, 0)} ({numeralWrapper.formatExp(props.member.cha_exp)} exp)
<br /> <br />
</pre> </Typography>
</Tooltip>
<br /> <br />
{props.member.canAscend() && ( {props.member.canAscend() && (
<> <>
<button className="accordion-button noselect" onClick={ascend}> <Button onClick={() => setAscendOpen(true)}>Ascend</Button>
Ascend <AscensionModal
</button> open={ascendOpen}
<div className="help-tip noselect" style={{ marginTop: "5px" }} onClick={openAscensionHelp}> onClose={() => setAscendOpen(false)}
? member={props.member}
</div> 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,42 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { ManagementSubpage } from "./ManagementSubpage"; import { ManagementSubpage } from "./ManagementSubpage";
import { TerritorySubpage } from "./TerritorySubpage"; import { TerritorySubpage } from "./TerritorySubpage";
import { EquipmentsSubpage } from "./EquipmentsSubpage";
import { use } from "../../ui/Context"; 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";
export function GangRoot(): React.ReactElement { export function GangRoot(): React.ReactElement {
const player = use.Player(); const player = use.Player();
const router = use.Router();
const gang = (function () { const gang = (function () {
if (player.gang === null) throw new Error("Gang should not be null"); if (player.gang === null) throw new Error("Gang should not be null");
return player.gang; 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]; const setRerender = useState(false)[1];
useEffect(() => { useEffect(() => {
const id = setInterval(() => setRerender((old) => !old), 1000); const id = setInterval(() => setRerender((old) => !old), 200);
return () => clearInterval(id); return () => clearInterval(id);
}, []); }, []);
function back(): void {
router.toFaction(Factions[gang.facName]);
}
return ( return (
<div className="gang-container"> <Context.Gang.Provider value={gang}>
<a className="a-link-button" style={{ display: "inline-block" }} onClick={back}> <Tabs variant="fullWidth" value={value} onChange={handleChange}>
Back <Tab label="Management" />
</a> <Tab label="Equipment" />
<a <Tab label="Territory" />
className={management ? "a-link-button-inactive" : "a-link-button"} </Tabs>
style={{ display: "inline-block" }} {value === 0 && <ManagementSubpage />}
onClick={() => setManagement(true)} {value === 1 && <EquipmentsSubpage />}
> {value === 2 && <TerritorySubpage />}
Gang Management </Context.Gang.Provider>
</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>
); );
} }

@ -4,7 +4,6 @@
*/ */
import React from "react"; import React from "react";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { Gang } from "../Gang";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
@ -12,13 +11,14 @@ import { MoneyRate } from "../../ui/React/MoneyRate";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { AllGangs } from "../AllGangs"; import { AllGangs } from "../AllGangs";
import { BonusTime } from "./BonusTime"; 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 { export function GangStats(): React.ReactElement {
gang: Gang; const gang = useGang();
} const territoryMult = AllGangs[gang.facName].territory * 100;
export function GangStats(props: IProps): React.ReactElement {
const territoryMult = AllGangs[props.gang.facName].territory * 100;
let territoryStr; let territoryStr;
if (territoryMult <= 0) { if (territoryMult <= 0) {
territoryStr = formatNumber(0, 2); territoryStr = formatNumber(0, 2);
@ -30,46 +30,59 @@ export function GangStats(props: IProps): React.ReactElement {
return ( return (
<> <>
<p className="tooltip" style={{ display: "inline-block" }}> <Box display="flex">
Respect: {numeralWrapper.formatRespect(props.gang.respect)} ( <Tooltip
{numeralWrapper.formatRespect(5 * props.gang.respectGainRate)} / sec) title={
<span className="tooltiptext"> <Typography>
Represents the amount of respect your gang has from other gangs and criminal organizations. Your respect 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 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. earning with your gang's corresponding Faction.
</span> </Typography>
</p> }
<br /> >
<p className="tooltip" style={{ display: "inline-block" }}> <Typography>
Wanted Level: {numeralWrapper.formatWanted(props.gang.wanted)} ( Respect: {numeralWrapper.formatRespect(gang.respect)} (
{numeralWrapper.formatWanted(5 * props.gang.wantedGainRate)} / sec) {numeralWrapper.formatRespect(5 * gang.respectGainRate)} / sec)
<span className="tooltiptext"> </Typography>
Represents how much the gang is wanted by law enforcement. The higher your gang's wanted level, the harder it </Tooltip>
will be for your gang members to make money and earn respect. Note that the minimum wanted level is 1. </Box>
</span>
</p> <Box display="flex">
<br /> <Tooltip
<p className="tooltip" style={{ display: "inline-block" }}> title={
Wanted Level Penalty: -{formatNumber((1 - props.gang.getWantedPenalty()) * 100, 2)}% <Typography>
<span className="tooltiptext">Penalty for respect and money gain rates due to Wanted Level</span> Represents how much the gang is wanted by law enforcement. The higher your gang's wanted level, the harder
</p> it will be for your gang members to make money and earn respect. Note that the minimum wanted level is 1.
<br /> </Typography>
<div> }
<p style={{ display: "inline-block" }}> >
Money gain rate: <MoneyRate money={5 * props.gang.moneyGainRate} /> <Typography>
</p> Wanted Level: {numeralWrapper.formatWanted(gang.wanted)} (
</div> {numeralWrapper.formatWanted(5 * gang.wantedGainRate)} / sec)
<br /> </Typography>
<p className="tooltip" style={{ display: "inline-block" }}> </Tooltip>
Territory: {territoryStr}% </Box>
<span className="tooltiptext">The percentage of total territory your Gang controls</span>
</p> <Box display="flex">
<br /> <Tooltip title={<Typography>Penalty for respect and money gain rates due to Wanted Level</Typography>}>
<p style={{ display: "inline-block" }}> <Typography>Wanted Level Penalty: -{formatNumber((1 - gang.getWantedPenalty()) * 100, 2)}%</Typography>
Faction reputation: <Reputation reputation={Factions[props.gang.facName].playerReputation} /> </Tooltip>
</p> </Box>
<br />
<BonusTime gang={props.gang} /> <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. * React Component for the subpage that manages gang members, the main page.
*/ */
import React from "react"; import React from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { GangStats } from "./GangStats"; import { GangStats } from "./GangStats";
import { Gang } from "../Gang";
import { GangMemberList } from "./GangMemberList"; import { GangMemberList } from "./GangMemberList";
import { useGang } from "./Context";
import Typography from "@mui/material/Typography";
interface IProps { export function ManagementSubpage(): React.ReactElement {
gang: Gang; const gang = useGang();
player: IPlayer;
}
export function ManagementSubpage(props: IProps): React.ReactElement {
return ( 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. This page is used to manage your gang members and get an overview of your gang's stats.
<br /> <br />
<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 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 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 ' 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 />
<br /> <br />
Installing Augmentations does NOT reset your progress with your Gang. Furthermore, after installing 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 />
<br /> <br />
You can also manage your gang programmatically through Netscript using the Gang API You can also manage your gang programmatically through Netscript using the Gang API
</p> </Typography>
<br /> <br />
<GangStats gang={props.gang} /> <GangStats />
<br /> <br />
<GangMemberList gang={props.gang} player={props.player} /> <GangMemberList />
</div> </>
); );
} }

@ -1,51 +1,42 @@
/** /**
* React Component for the recruitment button and text on the gang main page. * React Component for the recruitment button and text on the gang main page.
*/ */
import React from "react"; import React, { useState } from "react";
import { Gang } from "../Gang"; import { RecruitModal } from "./RecruitModal";
import { RecruitPopup } from "./RecruitPopup";
import { GangConstants } from "../data/Constants"; import { GangConstants } from "../data/Constants";
import { formatNumber } from "../../utils/StringHelperFunctions"; 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 { interface IProps {
gang: Gang;
onRecruit: () => void; onRecruit: () => void;
} }
export function RecruitButton(props: IProps): React.ReactElement { 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 <></>; return <></>;
} }
if (!props.gang.canRecruitMember()) { if (!gang.canRecruitMember()) {
const respect = props.gang.getRespectNeededToRecruitMember(); const respect = gang.getRespectNeededToRecruitMember();
return ( return (
<> <Box display="flex" alignItems="center">
<a className="a-link-button-inactive" style={{ display: "inline-block", margin: "10px" }}> <Button sx={{ mx: 1 }} disabled>
Recruit Gang Member Recruit Gang Member
</a> </Button>
<p style={{ margin: "10px", color: "red", display: "inline-block" }}> <Typography>{formatNumber(respect, 2)} respect needed to recruit next member</Typography>
{formatNumber(respect, 2)} respect needed to recruit next member </Box>
</p>
</>
); );
} }
function onClick(): void {
const popupId = "recruit-gang-member-popup";
createPopup(popupId, RecruitPopup, {
gang: props.gang,
popupId: popupId,
onRecruit: props.onRecruit,
});
}
return ( return (
<> <>
<a className="a-link-button" onClick={onClick} style={{ display: "inline-block", margin: "10px" }}> <Button onClick={() => setOpen(true)}>Recruit Gang Member</Button>
Recruit Gang Member <RecruitModal open={open} onClose={() => setOpen(false)} onRecruit={props.onRecruit} />
</a>
</> </>
); );
} }

@ -2,38 +2,35 @@
* React Component for the popup used to recruit new gang members. * React Component for the popup used to recruit new gang members.
*/ */
import React, { useState } from "react"; import React, { useState } from "react";
import { Gang } from "../Gang"; import { Modal } from "../../ui/React/Modal";
import { removePopup } from "../../ui/React/createPopup";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; 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 { interface IRecruitPopupProps {
gang: Gang; open: boolean;
popupId: string; onClose: () => void;
onRecruit: () => 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 [name, setName] = useState("");
const disabled = name === "" || !gang.canRecruitMember();
function recruit(): void { function recruit(): void {
if (name === "") { if (disabled) return;
dialogBoxCreate("You must enter a name for your Gang member!");
return;
}
if (!props.gang.canRecruitMember()) {
dialogBoxCreate("You cannot recruit another Gang member!");
return;
}
// At this point, the only way this can fail is if you already // At this point, the only way this can fail is if you already
// have a gang member with the same name // 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!"); dialogBoxCreate("You already have a gang member with this name!");
return; return;
} }
props.onRecruit(); props.onRecruit();
removePopup(props.popupId); props.onClose();
} }
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void { function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
@ -45,20 +42,23 @@ export function RecruitPopup(props: IRecruitPopupProps): React.ReactElement {
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p className="noselect">Enter a name for your new Gang member:</p> <Typography>Enter a name for your new Gang member:</Typography>
<br /> <br />
<input <TextField
autoFocus autoFocus
onKeyUp={onKeyUp} onKeyUp={onKeyUp}
onChange={onChange} onChange={onChange}
className="text-input noselect"
type="text" type="text"
placeholder="unique name" placeholder="unique name"
InputProps={{
endAdornment: (
<Button disabled={disabled} onClick={recruit}>
Recruit
</Button>
),
}}
/> />
<a className="std-button" onClick={recruit}> </Modal>
Recruit Gang Member
</a>
</>
); );
} }

@ -5,6 +5,7 @@
import React from "react"; import React from "react";
import { GangMemberTasks } from "../GangMemberTasks"; import { GangMemberTasks } from "../GangMemberTasks";
import { GangMember } from "../GangMember"; import { GangMember } from "../GangMember";
import Typography from "@mui/material/Typography";
interface IProps { interface IProps {
member: GangMember; member: GangMember;
@ -14,5 +15,5 @@ export function TaskDescription(props: IProps): React.ReactElement {
const task = GangMemberTasks[props.member.task]; const task = GangMemberTasks[props.member.task];
const desc = task ? task.desc : GangMemberTasks["Unassigned"].desc; 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 { numeralWrapper } from "../../ui/numeralFormat";
import { StatsTable } from "../../ui/React/StatsTable"; import { StatsTable } from "../../ui/React/StatsTable";
import { MoneyRate } from "../../ui/React/MoneyRate"; import { MoneyRate } from "../../ui/React/MoneyRate";
import { Gang } from "../Gang"; import { useGang } from "./Context";
import { GangMember } from "../GangMember"; import { GangMember } from "../GangMember";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
interface IProps { interface IProps {
member: GangMember; member: GangMember;
gang: Gang;
onTaskChange: () => void; onTaskChange: () => void;
} }
export function TaskSelector(props: IProps): React.ReactElement { export function TaskSelector(props: IProps): React.ReactElement {
const gang = useGang();
const [currentTask, setCurrentTask] = useState(props.member.task); 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; const task = event.target.value;
props.member.assignToTask(task); props.member.assignToTask(task);
setCurrentTask(task); setCurrentTask(task);
props.onTaskChange(); props.onTaskChange();
} }
const tasks = props.gang.getAllTaskNames(); const tasks = gang.getAllTaskNames();
const data = [ const data = [
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(props.gang)} />], [`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(gang)} />],
[`Respect:`, `${numeralWrapper.formatRespect(5 * props.member.calculateRespectGain(props.gang))} / sec`], [`Respect:`, `${numeralWrapper.formatRespect(5 * props.member.calculateRespectGain(gang))} / sec`],
[`Wanted Level:`, `${numeralWrapper.formatWanted(5 * props.member.calculateWantedLevelGain(props.gang))} / sec`], [`Wanted Level:`, `${numeralWrapper.formatWanted(5 * props.member.calculateWantedLevelGain(gang))} / sec`],
[`Total Respect:`, `${numeralWrapper.formatRespect(props.member.earnedRespect)}`], [`Total Respect:`, `${numeralWrapper.formatRespect(props.member.earnedRespect)}`],
]; ];
return ( return (
<> <>
<select onChange={onChange} className="dropdown noselect" value={currentTask}> <Select onChange={onChange} value={currentTask}>
<option key={0} value={"---"}> <MenuItem key={0} value={"Unassigned"}>
--- Unassigned
</option> </MenuItem>
{tasks.map((task: string, i: number) => ( {tasks.map((task: string, i: number) => (
<option key={i + 1} value={task}> <MenuItem key={i + 1} value={task}>
{task} {task}
</option> </MenuItem>
))} ))}
</select> </Select>
<div>
<StatsTable rows={data} /> <StatsTable rows={data} />
</div>
</> </>
); );
} }

@ -3,59 +3,24 @@
*/ */
import React from "react"; import React from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import { AllGangs } from "../AllGangs"; import { AllGangs } from "../AllGangs";
import { Gang } from "../Gang"; import { useGang } from "./Context";
interface IProps { import Typography from "@mui/material/Typography";
gang: Gang; 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 { export function TerritorySubpage(): React.ReactElement {
function openWarfareHelp(): void { const gang = useGang();
dialogBoxCreate( const gangNames = Object.keys(AllGangs).filter((g) => g != gang.facName);
"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);
return ( 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 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. represents how much of the total territory you control.
<br /> <br />
@ -76,59 +41,113 @@ export function TerritorySubpage(props: IProps): React.ReactElement {
and wanted level. It is very beneficial to have high territory control. and wanted level. It is very beneficial to have high territory control.
<br /> <br />
<br /> <br />
</p> </Typography>
<input <FormControlLabel
checked={props.gang.territoryWarfareEngaged} control={
id="warfare" <Switch
type="checkbox" checked={gang.territoryWarfareEngaged}
style={{ display: "inline-block", margin: "2px" }} onChange={(event) => (gang.territoryWarfareEngaged = event.target.checked)}
onChange={(event) => (props.gang.territoryWarfareEngaged = event.target.checked)}
/> />
<label htmlFor="warfare" className="tooltip noselect" style={{ color: "white", display: "inline-block" }}> }
Engage in Territory Warfare label={
<span className="tooltiptext" style={{ display: "inline-block" }}> <Tooltip
Engaging in Territory Warfare sets your clash chance to 100%. Disengaging will cause your clash chance to title={
gradually decrease until it reaches 0%. <Typography>
</span> Engaging in Territory Warfare sets your clash chance to 100%. Disengaging will cause your clash chance
</label> to gradually decrease until it reaches 0%.
<br /> </Typography>
<p style={{ display: "inline-block" }}> }
Territory Clash Chance: {numeralWrapper.formatPercentage(props.gang.territoryClashChance, 3)} >
</p> <Typography>Engage in Territory Warfare</Typography>
<div className="help-tip noselect" style={{ display: "inline-block" }} onClick={openWarfareHelp}> </Tooltip>
? }
</div>
<br />
<input
checked={props.gang.notifyMemberDeath}
id="notify"
type="checkbox"
style={{ display: "inline-block", margin: "2px" }}
onChange={(event) => (props.gang.notifyMemberDeath = event.target.checked)}
/> />
<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 /> <br />
<fieldset style={{ display: "block", margin: "6px" }}> <Box display="flex">
<p> <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 />
<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>
}
/>
<br />
<Paper>
<Typography>
<b> <b>
<u>{props.gang.facName}</u> <u>{gang.facName}</u>
</b> </b>
<br /> <br />
Power: {formatNumber(AllGangs[props.gang.facName].power, 6)} Power: {formatNumber(AllGangs[gang.facName].power, 6)}
<br /> <br />
Territory: {formatTerritory(AllGangs[props.gang.facName].territory)}% Territory: {formatTerritory(AllGangs[gang.facName].territory)}%
<br /> <br />
<br /> <br />
{gangNames.map((name) => otherGangTerritory(name))} </Typography>
</p> {gangNames.map((name) => (
</fieldset> <OtherGangTerritory key={name} name={name} />
</div> ))}
</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>
); );
} }

@ -1,13 +0,0 @@
import React, { useEffect } from "react";
import { startHackingMission } from "../../Faction/FactionHelpers";
import { Faction } from "../../Faction/Faction";
interface IProps {
faction: Faction;
}
export function HackingMissionRoot(props: IProps): React.ReactElement {
useEffect(() => startHackingMission(props.faction));
return <div id="mission-container"></div>;
}

@ -7,7 +7,6 @@ import { hasHacknetServers, hasMaxNumberHacknetServers } from "../HacknetHelpers
import { Player } from "../../Player"; import { Player } from "../../Player";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
interface IProps { interface IProps {

@ -23,10 +23,6 @@ type IProps = {
p: IPlayer; p: IPlayer;
}; };
type IState = {
game: GameType;
};
export function CasinoLocation(props: IProps): React.ReactElement { export function CasinoLocation(props: IProps): React.ReactElement {
const [game, setGame] = useState(GameType.None); const [game, setGame] = useState(GameType.None);

@ -6,7 +6,6 @@ import { purchaseServer } from "../../Server/ServerPurchases";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { StdButton } from "../../ui/React/StdButton";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";

@ -1,5 +1,4 @@
import React from "react"; import React from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";

@ -2,7 +2,6 @@ import { Message } from "./Message";
import { Augmentations } from "../Augmentation/Augmentations"; import { Augmentations } from "../Augmentation/Augmentations";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { Programs } from "../Programs/Programs"; import { Programs } from "../Programs/Programs";
import { inMission } from "../Missions";
import { Player } from "../Player"; import { Player } from "../Player";
import { redPillFlag } from "../RedPill"; import { redPillFlag } from "../RedPill";
import { GetServerByHostname } from "../Server/ServerHelpers"; import { GetServerByHostname } from "../Server/ServerHelpers";
@ -66,11 +65,11 @@ function checkForMessagesToSend(): void {
redpillOwned = true; redpillOwned = true;
} }
if (redpill && redpillOwned && Player.sourceFiles.length === 0 && !redPillFlag && !inMission) { if (redpill && redpillOwned && Player.sourceFiles.length === 0 && !redPillFlag) {
sendMessage(redpill, true); sendMessage(redpill, true);
} else if (redpill && redpillOwned) { } else if (redpill && redpillOwned) {
//If player has already destroyed a BitNode, message is not forced //If player has already destroyed a BitNode, message is not forced
if (!redPillFlag && !inMission) { if (!redPillFlag) {
sendMessage(redpill); sendMessage(redpill);
} }
} else if (jumper0 && !jumper0.recvd && Player.hacking_skill >= 25) { } else if (jumper0 && !jumper0.recvd && Player.hacking_skill >= 25) {

8
src/Missions.d.ts vendored

@ -1,8 +0,0 @@
export declare let inMission: boolean;
export declare class HackingMission {
constructor(reputation: number, faction: Faction);
init(): void;
process(numCycles: number): void;
}
export declare function setInMission(inMission: boolean, mission: HackingMission): void;
export declare let currMission: HackingMission;

File diff suppressed because it is too large Load Diff

@ -98,7 +98,6 @@ import { Terminal } from "./Terminal";
import { calculateSkill, calculateExp } from "./PersonObjects/formulas/skill"; import { calculateSkill, calculateExp } from "./PersonObjects/formulas/skill";
import { Message } from "./Message/Message"; import { Message } from "./Message/Message";
import { inMission } from "./Missions";
import { Player } from "./Player"; import { Player } from "./Player";
import { Programs } from "./Programs/Programs"; import { Programs } from "./Programs/Programs";
import { Script } from "./Script/Script"; import { Script } from "./Script/Script";
@ -2965,10 +2964,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
universityCourse: function (universityName: any, className: any): any { universityCourse: function (universityName: any, className: any): any {
updateDynamicRam("universityCourse", getRamCost("universityCourse")); updateDynamicRam("universityCourse", getRamCost("universityCourse"));
checkSingularityAccess("universityCourse", 1); checkSingularityAccess("universityCourse", 1);
if (inMission) {
workerScript.log("universityCourse", "You are in the middle of a mission.");
return;
}
if (Player.isWorking) { if (Player.isWorking) {
const txt = Player.singularityStopWork(); const txt = Player.singularityStopWork();
workerScript.log("universityCourse", txt); workerScript.log("universityCourse", txt);
@ -3049,10 +3044,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
gymWorkout: function (gymName: any, stat: any): any { gymWorkout: function (gymName: any, stat: any): any {
updateDynamicRam("gymWorkout", getRamCost("gymWorkout")); updateDynamicRam("gymWorkout", getRamCost("gymWorkout"));
checkSingularityAccess("gymWorkout", 1); checkSingularityAccess("gymWorkout", 1);
if (inMission) {
workerScript.log("gymWorkout", "You are in the middle of a mission.");
return;
}
if (Player.isWorking) { if (Player.isWorking) {
const txt = Player.singularityStopWork(); const txt = Player.singularityStopWork();
workerScript.log("gymWorkout", txt); workerScript.log("gymWorkout", txt);
@ -3486,7 +3477,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
isBusy: function (): any { isBusy: function (): any {
updateDynamicRam("isBusy", getRamCost("isBusy")); updateDynamicRam("isBusy", getRamCost("isBusy"));
checkSingularityAccess("isBusy", 1); checkSingularityAccess("isBusy", 1);
return Player.isWorking || inMission; return Player.isWorking;
}, },
stopAction: function (): any { stopAction: function (): any {
updateDynamicRam("stopAction", getRamCost("stopAction")); updateDynamicRam("stopAction", getRamCost("stopAction"));
@ -3553,12 +3544,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
return false; return false;
} }
// Cant work while in a mission
if (inMission) {
workerScript.log("workForCompany", "You are in the middle of a mission.");
return false;
}
// Check to make sure company position data is valid // Check to make sure company position data is valid
const companyPositionName = Player.jobs[companyName]; const companyPositionName = Player.jobs[companyName];
const companyPosition = CompanyPositions[companyPositionName]; const companyPosition = CompanyPositions[companyPositionName];
@ -3708,11 +3693,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
return; return;
} }
if (inMission) {
workerScript.log("workForFaction", "You are in the middle of a mission.");
return;
}
if (!Player.factions.includes(name)) { if (!Player.factions.includes(name)) {
workerScript.log("workForFaction", `You are not a member of '${name}'`); workerScript.log("workForFaction", `You are not a member of '${name}'`);
return false; return false;
@ -3900,10 +3880,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
updateDynamicRam("createProgram", getRamCost("createProgram")); updateDynamicRam("createProgram", getRamCost("createProgram"));
checkSingularityAccess("createProgram", 3); checkSingularityAccess("createProgram", 3);
if (inMission) {
workerScript.log("createProgram", "You are in the middle of a mission.");
return;
}
if (Player.isWorking) { if (Player.isWorking) {
const txt = Player.singularityStopWork(); const txt = Player.singularityStopWork();
workerScript.log("createProgram", txt); workerScript.log("createProgram", txt);
@ -3946,10 +3922,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
commitCrime: function (crimeRoughName: any): any { commitCrime: function (crimeRoughName: any): any {
updateDynamicRam("commitCrime", getRamCost("commitCrime")); updateDynamicRam("commitCrime", getRamCost("commitCrime"));
checkSingularityAccess("commitCrime", 3); checkSingularityAccess("commitCrime", 3);
if (inMission) {
workerScript.log("commitCrime", "You are in the middle of a mission.");
return;
}
if (Player.isWorking) { if (Player.isWorking) {
const txt = Player.singularityStopWork(); const txt = Player.singularityStopWork();
workerScript.log("commitCrime", txt); workerScript.log("commitCrime", txt);

@ -283,7 +283,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: bitFlumeRequirements(), req: bitFlumeRequirements(),
time: CONSTANTS.MillisecondsPerFiveMinutes / 20, time: CONSTANTS.MillisecondsPerFiveMinutes / 20,
}, },
run: (router: IRouter, terminal: ITerminal, player: IPlayer): void => { run: (): void => {
BitFlumeEvent.emit(); BitFlumeEvent.emit();
}, },
}, },

@ -50,7 +50,6 @@ import { getAvailableCreatePrograms } from "../../Programs/ProgramHelpers";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { redPillFlag } from "../../RedPill"; import { redPillFlag } from "../../RedPill";
import { inMission } from "../../Missions";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../utils/helpers/keyCodes";
const openedMixin = (theme: Theme): CSSObject => ({ const openedMixin = (theme: Theme): CSSObject => ({
@ -266,7 +265,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
// Alt-o - Options // Alt-o - Options
function handleShortcuts(this: Document, event: KeyboardEvent): any { function handleShortcuts(this: Document, event: KeyboardEvent): any {
if (Settings.DisableHotkeys) return; if (Settings.DisableHotkeys) return;
if (props.player.isWorking || redPillFlag || inMission) return; if (props.player.isWorking || redPillFlag) return;
if (event.keyCode == KEY.T && event.altKey) { if (event.keyCode == KEY.T && event.altKey) {
event.preventDefault(); event.preventDefault();
clickTerminal(); clickTerminal();

@ -23,7 +23,6 @@ import {
import { hasHacknetServers, processHacknetEarnings } from "./Hacknet/HacknetHelpers"; import { hasHacknetServers, processHacknetEarnings } from "./Hacknet/HacknetHelpers";
import { iTutorialStart } from "./InteractiveTutorial"; import { iTutorialStart } from "./InteractiveTutorial";
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers"; import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
import { inMission, currMission } from "./Missions";
import { loadAllRunningScripts, updateOnlineScriptTimes } from "./NetscriptWorker"; import { loadAllRunningScripts, updateOnlineScriptTimes } from "./NetscriptWorker";
import { Player } from "./Player"; import { Player } from "./Player";
import { saveObject, loadGame } from "./SaveObject"; import { saveObject, loadGame } from "./SaveObject";
@ -131,11 +130,6 @@ const Engine: {
Player.gang.process(numCycles, Player); Player.gang.process(numCycles, Player);
} }
// Mission
if (inMission && currMission) {
currMission.process(numCycles);
}
// Corporation // Corporation
if (Player.corporation instanceof Corporation) { if (Player.corporation instanceof Corporation) {
// Stores cycles in a "buffer". Processed separately using Engine Counters // Stores cycles in a "buffer". Processed separately using Engine Counters

@ -56,7 +56,6 @@ import { TerminalRoot } from "../Terminal/ui/TerminalRoot";
import { TutorialRoot } from "../Tutorial/ui/TutorialRoot"; import { TutorialRoot } from "../Tutorial/ui/TutorialRoot";
import { ActiveScriptsRoot } from "../ui/ActiveScripts/ActiveScriptsRoot"; import { ActiveScriptsRoot } from "../ui/ActiveScripts/ActiveScriptsRoot";
import { FactionsRoot } from "../Faction/ui/FactionsRoot"; import { FactionsRoot } from "../Faction/ui/FactionsRoot";
import { HackingMissionRoot } from "../HackingMission/ui/HackingMissionRoot";
import { FactionRoot } from "../Faction/ui/FactionRoot"; import { FactionRoot } from "../Faction/ui/FactionRoot";
import { CharacterStats } from "./CharacterStats"; import { CharacterStats } from "./CharacterStats";
import { TravelAgencyRoot } from "../Locations/ui/TravelAgencyRoot"; import { TravelAgencyRoot } from "../Locations/ui/TravelAgencyRoot";
@ -178,9 +177,6 @@ export let Router: IRouter = {
toLocation: () => { toLocation: () => {
throw new Error("Router called before initialization"); throw new Error("Router called before initialization");
}, },
toHackingMission: () => {
throw new Error("Router called before initialization");
},
}; };
function determineStartPage(player: IPlayer): Page { function determineStartPage(player: IPlayer): Page {
@ -270,10 +266,6 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
setLocation(location); setLocation(location);
setPage(Page.Location); setPage(Page.Location);
}, },
toHackingMission: (faction: Faction) => {
setPage(Page.HackingMission);
setFaction(faction);
},
}; };
useEffect(() => { useEffect(() => {
@ -296,8 +288,6 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
<BitverseRoot flume={flume} enter={enterBitNode} quick={quick} /> <BitverseRoot flume={flume} enter={enterBitNode} quick={quick} />
) : page === Page.Infiltration ? ( ) : page === Page.Infiltration ? (
<InfiltrationRoot location={location} /> <InfiltrationRoot location={location} />
) : page === Page.HackingMission ? (
<HackingMissionRoot faction={faction} />
) : page === Page.BladeburnerCinematic ? ( ) : page === Page.BladeburnerCinematic ? (
<BladeburnerCinematic /> <BladeburnerCinematic />
) : page === Page.Work ? ( ) : page === Page.Work ? (

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

@ -4,6 +4,7 @@ import Tooltip from "@mui/material/Tooltip";
type IProps = { type IProps = {
value: string; value: string;
color?: string;
variant?: variant?:
| "button" | "button"
| "caption" | "caption"
@ -39,7 +40,7 @@ export function CopyableText(props: IProps): React.ReactElement {
return ( return (
<Tooltip open={open} title={<Typography>Copied!</Typography>}> <Tooltip open={open} title={<Typography>Copied!</Typography>}>
<Typography variant={props.variant} onClick={copy}> <Typography variant={props.variant} color={props.color} onClick={copy}>
{props.value} {props.value}
</Typography> </Typography>
</Tooltip> </Tooltip>

@ -1,7 +1,6 @@
import { AlertEvents } from "./AlertManager"; import { AlertEvents } from "./AlertManager";
import React from "react"; import React from "react";
import { jsx } from "@emotion/react";
export function dialogBoxCreate(txt: string | JSX.Element): void { export function dialogBoxCreate(txt: string | JSX.Element): void {
if (typeof txt !== "string") { if (typeof txt !== "string") {

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

@ -23,8 +23,7 @@ export function Overview({ children }: IProps): React.ReactElement {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
const classes = useStyles(); const classes = useStyles();
const router = use.Router(); const router = use.Router();
if (router.page() === Page.BitVerse || router.page() === Page.HackingMission || router.page() === Page.Loading) if (router.page() === Page.BitVerse || router.page() === Page.Loading) return <></>;
return <></>;
let icon; let icon;
if (open) { if (open) {
icon = <VisibilityOffIcon color="primary" />; icon = <VisibilityOffIcon color="primary" />;

@ -16,7 +16,7 @@ export function Reputation({ reputation }: { reputation: number | string }): Rea
const classes = useStyles(); const classes = useStyles();
return ( return (
<span className={classes.reputation}> <span className={classes.reputation}>
{typeof reputation === "number" ? numeralWrapper.formatFavor(reputation) : reputation} {typeof reputation === "number" ? numeralWrapper.formatReputation(reputation) : reputation}
</span> </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>
);
}

@ -33,7 +33,6 @@ export enum Page {
Work, Work,
BladeburnerCinematic, BladeburnerCinematic,
Location, Location,
HackingMission,
Loading, Loading,
} }
@ -74,5 +73,4 @@ export interface IRouter {
toWork(): void; toWork(): void;
toBladeburnerCinematic(): void; toBladeburnerCinematic(): void;
toLocation(location: Location): void; toLocation(location: Location): void;
toHackingMission(faction: Faction): void;
} }