diff --git a/src/Gang/GangMemberUpgrade.ts b/src/Gang/GangMemberUpgrade.ts index 7cfd6c291..aa602bc13 100644 --- a/src/Gang/GangMemberUpgrade.ts +++ b/src/Gang/GangMemberUpgrade.ts @@ -1,16 +1,15 @@ -import { IMults } from "./data/upgrades"; +import { IMults, UpgradeType } from "./data/upgrades"; export class GangMemberUpgrade { name: string; cost: number; - type: string; + type: UpgradeType; desc: string; mults: IMults; - constructor(name = "", cost = 0, type = "w", mults: IMults = {}) { + constructor(name = "", cost = 0, type: UpgradeType = UpgradeType.Weapon, mults: IMults = {}) { this.name = name; this.cost = cost; - //w = weapon, a = armor, v = vehicle, r = rootkit, g = Aug this.type = type; this.mults = mults; @@ -43,15 +42,15 @@ export class GangMemberUpgrade { // User friendly version of type. getType(): string { switch (this.type) { - case "w": + case UpgradeType.Weapon: return "Weapon"; - case "a": + case UpgradeType.Armor: return "Armor"; - case "v": + case UpgradeType.Vehicle: return "Vehicle"; - case "r": + case UpgradeType.Rootkit: return "Rootkit"; - case "g": + case UpgradeType.Augmentation: return "Augmentation"; default: return ""; diff --git a/src/Gang/IAscensionResult.ts b/src/Gang/IAscensionResult.ts index f42f7c603..224573cd1 100644 --- a/src/Gang/IAscensionResult.ts +++ b/src/Gang/IAscensionResult.ts @@ -6,4 +6,4 @@ export interface IAscensionResult { dex: number; agi: number; cha: number; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/Gang/data/upgrades.ts b/src/Gang/data/upgrades.ts index 73d0d52e0..7b4b80dd0 100644 --- a/src/Gang/data/upgrades.ts +++ b/src/Gang/data/upgrades.ts @@ -7,6 +7,14 @@ export interface IMults { cha?: number; } +export enum UpgradeType { + Weapon = "w", + Armor = "a", + Vehicle = "v", + Rootkit = "r", + Augmentation = "g", +} + /** * Defines the parameters that can be used to initialize and describe a GangMemberUpgrade * (defined in Gang.js) @@ -15,7 +23,7 @@ export interface IGangMemberUpgradeMetadata { cost: number; mults: IMults; name: string; - upgType: string; + upgType: UpgradeType; } /** @@ -27,192 +35,192 @@ export const gangMemberUpgradesMetadata: IGangMemberUpgradeMetadata[] = [ cost: 1e6, mults: {str: 1.04, def: 1.04}, name: "Baseball Bat", - upgType: "w", + upgType: UpgradeType.Weapon, }, { cost: 12e6, mults: {str: 1.08, def: 1.08, dex: 1.08}, name: "Katana", - upgType: "w", + upgType: UpgradeType.Weapon, }, { cost: 25e6, mults: {str: 1.1, def: 1.1, dex: 1.1, agi: 1.1}, name: "Glock 18C", - upgType: "w", + upgType: UpgradeType.Weapon, }, { cost: 50e6, mults: {str: 1.12, def: 1.1, agi: 1.1}, name: "P90C", - upgType: "w", + upgType: UpgradeType.Weapon, }, { cost: 60e6, mults: {str: 1.2, def: 1.15}, name: "Steyr AUG", - upgType: "w", + upgType: UpgradeType.Weapon, }, { cost: 100e6, mults: {str: 1.25, def: 1.2}, name: "AK-47", - upgType: "w", + upgType: UpgradeType.Weapon, }, { cost: 150e6, mults: {str: 1.3, def: 1.25}, name: "M15A10 Assault Rifle", - upgType: "w", + upgType: UpgradeType.Weapon, }, { cost: 225e6, mults: {str: 1.3, dex: 1.25, agi: 1.3}, name: "AWM Sniper Rifle", - upgType: "w", + upgType: UpgradeType.Weapon, }, { cost: 2e6, mults: {def: 1.04}, name: "Bulletproof Vest", - upgType: "a", + upgType: UpgradeType.Armor, }, { cost: 5e6, mults: {def: 1.08}, name: "Full Body Armor", - upgType: "a", + upgType: UpgradeType.Armor, }, { cost: 25e6, mults: {def: 1.15, agi: 1.15}, name: "Liquid Body Armor", - upgType: "a", + upgType: UpgradeType.Armor, }, { cost: 40e6, mults: {def: 1.2}, name: "Graphene Plating Armor", - upgType: "a", + upgType: UpgradeType.Armor, }, { cost: 3e6, mults: {agi: 1.04, cha: 1.04}, name: "Ford Flex V20", - upgType: "v", + upgType: UpgradeType.Vehicle, }, { cost: 9e6, mults: {agi: 1.08, cha: 1.08}, name: "ATX1070 Superbike", - upgType: "v", + upgType: UpgradeType.Vehicle, }, { cost: 18e6, mults: {agi: 1.12, cha: 1.12}, name: "Mercedes-Benz S9001", - upgType: "v", + upgType: UpgradeType.Vehicle, }, { cost: 30e6, mults: {agi: 1.16, cha: 1.16}, name: "White Ferrari", - upgType: "v", + upgType: UpgradeType.Vehicle, }, { cost: 5e6, mults: {hack: 1.05}, name: "NUKE Rootkit", - upgType: "r", + upgType: UpgradeType.Rootkit, }, { cost: 25e6, mults: {hack: 1.1}, name: "Soulstealer Rootkit", - upgType: "r", + upgType: UpgradeType.Rootkit, }, { cost: 75e6, mults: {hack: 1.15}, name: "Demon Rootkit", - upgType: "r", + upgType: UpgradeType.Rootkit, }, { cost: 40e6, mults: {hack: 1.12}, name: "Hmap Node", - upgType: "r", + upgType: UpgradeType.Rootkit, }, { cost: 75e6, mults: {hack: 1.15}, name: "Jack the Ripper", - upgType: "r", + upgType: UpgradeType.Rootkit, }, { cost: 10e9, mults: {str: 1.3, dex: 1.3}, name: "Bionic Arms", - upgType: "g", + upgType: UpgradeType.Augmentation, }, { cost: 10e9, mults: {agi: 1.6}, name: "Bionic Legs", - upgType: "g", + upgType: UpgradeType.Augmentation, }, { cost: 15e9, mults: {str: 1.15, def: 1.15, dex: 1.15, agi: 1.15}, name: "Bionic Spine", - upgType: "g", + upgType: UpgradeType.Augmentation, }, { cost: 20e9, mults: {str: 1.4, def: 1.4}, name: "BrachiBlades", - upgType: "g", + upgType: UpgradeType.Augmentation, }, { cost: 12e9, mults: {str: 1.2, def: 1.2}, name: "Nanofiber Weave", - upgType: "g", + upgType: UpgradeType.Augmentation, }, { cost: 25e9, mults: {str: 1.5, agi: 1.5}, name: "Synthetic Heart", - upgType: "g", + upgType: UpgradeType.Augmentation, }, { cost: 15e9, mults: {str: 1.3, def: 1.3}, name: "Synfibril Muscle", - upgType: "g", + upgType: UpgradeType.Augmentation, }, { cost: 5e9, mults: {hack: 1.05}, name: "BitWire", - upgType: "g", + upgType: UpgradeType.Augmentation, }, { cost: 10e9, mults: {hack: 1.15}, name: "Neuralstimulator", - upgType: "g", + upgType: UpgradeType.Augmentation, }, { cost: 7.5e9, mults: {hack: 1.1}, name: "DataJack", - upgType: "g", + upgType: UpgradeType.Augmentation, }, { cost: 50e9, mults: {str: 1.7, def: 1.7}, name: "Graphene Bone Lacings", - upgType: "g", + upgType: UpgradeType.Augmentation, }, ]; diff --git a/src/Gang/ui/AscensionPopup.tsx b/src/Gang/ui/AscensionPopup.tsx new file mode 100644 index 000000000..622d85222 --- /dev/null +++ b/src/Gang/ui/AscensionPopup.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { Gang } from "../Gang"; +import { GangMember } from "../GangMember"; +import { numeralWrapper } from "../../ui/numeralFormat"; +import { removePopup } from "../../ui/React/createPopup"; + +interface IProps { + member: GangMember; + gang: Gang; + popupId: string; +} + +export function AscensionPopup(props: IProps): React.ReactElement { + function confirm(): void { + props.gang.ascendMember(props.member); + removePopup(props.popupId); + } + + function cancel(): void { + removePopup(props.popupId); + } + + const ascendBenefits = props.member.getAscensionResults(); + + return (<> +
+Are you sure you want to ascend this member? They will lose all of
+their non-Augmentation upgrades and their stats will reset back to 1.
+
+Furthermore, your gang will lose {numeralWrapper.formatRespect(props.member.earnedRespect)} respect
+
+In return, they will gain the following permanent boost to stat multipliers:
+Hacking: +{numeralWrapper.formatPercentage(ascendBenefits.hack/100)}
+Strength: +{numeralWrapper.formatPercentage(ascendBenefits.str/100)}
+Defense: +{numeralWrapper.formatPercentage(ascendBenefits.def/100)}
+Dexterity: +{numeralWrapper.formatPercentage(ascendBenefits.dex/100)}
+Agility: +{numeralWrapper.formatPercentage(ascendBenefits.agi/100)}
+Charisma: +{numeralWrapper.formatPercentage(ascendBenefits.cha/100)}
+
+ + + ); +} \ No newline at end of file diff --git a/src/Gang/ui/BonusTime.tsx b/src/Gang/ui/BonusTime.tsx new file mode 100644 index 000000000..693964daf --- /dev/null +++ b/src/Gang/ui/BonusTime.tsx @@ -0,0 +1,26 @@ +import * as React from "react"; +import { Gang } from "../Gang"; +import { CONSTANTS } from "../../Constants"; +import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; + +interface IProps { + gang: Gang; +} + +export function BonusTime(props: IProps): React.ReactElement { + const CyclerPerSecond = 1000 / CONSTANTS._idleSpeed; + if (props.gang.storedCycles / CyclerPerSecond*1000 <= 5000) return (<>); + const bonusMillis = props.gang.storedCycles / CyclerPerSecond * 1000; + return (<> +

+ Bonus time: {convertTimeMsToTimeElapsedString(bonusMillis)} + + 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. + +

+
+ ); +} \ No newline at end of file diff --git a/src/Gang/ui/GangMemberAccordion.tsx b/src/Gang/ui/GangMemberAccordion.tsx new file mode 100644 index 000000000..e2daa32eb --- /dev/null +++ b/src/Gang/ui/GangMemberAccordion.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { Gang } from "../Gang"; +import { GangMember } from "../GangMember"; +import { Accordion } from "../../ui/React/Accordion"; +import { GangMemberAccordionContent } from "./GangMemberAccordionContent"; + +interface IProps { + gang: Gang; + member: GangMember; +} + +export function GangMemberAccordion(props: IProps): React.ReactElement { + return {props.member.name}} + panelContent={} /> +} \ No newline at end of file diff --git a/src/Gang/ui/GangMemberAccordionContent.tsx b/src/Gang/ui/GangMemberAccordionContent.tsx index 573f95738..4b0ca2d08 100644 --- a/src/Gang/ui/GangMemberAccordionContent.tsx +++ b/src/Gang/ui/GangMemberAccordionContent.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; -import { Panel1 } from "./Panel1"; -import { Panel2 } from "./Panel2"; -import { Panel3 } from "./Panel3"; +import { GangMemberStats } from "./GangMemberStats"; +import { TaskSelector } from "./TaskSelector"; +import { TaskDescription } from "./TaskDescription"; import { Gang } from "../Gang"; import { GangMember } from "../GangMember"; @@ -14,13 +14,16 @@ export function GangMemberAccordionContent(props: IProps): React.ReactElement { const setRerender = useState(false)[1]; return (<>
- +
- setRerender(old => !old)} gang={props.gang} member={props.member} /> + setRerender(old => !old)} + gang={props.gang} + member={props.member} />
- +
); } diff --git a/src/Gang/ui/GangMemberList.tsx b/src/Gang/ui/GangMemberList.tsx index 3accc7163..a7eee3edd 100644 --- a/src/Gang/ui/GangMemberList.tsx +++ b/src/Gang/ui/GangMemberList.tsx @@ -1,11 +1,11 @@ -import React, { useState, useEffect } from "react"; -import { Accordion } from "../../ui/React/Accordion"; -import { GangMemberAccordionContent } from "./GangMemberAccordionContent" -import { GangMemberUpgradePopup } from "./GangMemberUpgradePopup" +import React, { useState } from "react"; +import { GangMemberUpgradePopup } from "./GangMemberUpgradePopup"; +import { GangMemberAccordion } from "./GangMemberAccordion"; import { createPopup } from "../../ui/React/createPopup"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { Gang } from "../Gang"; import { GangMember } from "../GangMember"; +import { RecruitButton } from "./RecruitButton"; interface IProps { gang: Gang; @@ -14,6 +14,7 @@ interface IProps { export function GangMemberList(props: IProps): React.ReactElement { const [filter, setFilter] = useState(""); + const setRerender = useState(false)[1]; function openUpgradePopup(): void { const popupId = `gang-upgrade-popup`; @@ -28,19 +29,27 @@ export function GangMemberList(props: IProps): React.ReactElement { setFilter(event.target.value); } - function members(): GangMember[] { - return props.gang.members.filter((member: GangMember) => member.name.indexOf(filter) > -1 || member.task.indexOf(filter) > -1) - } + const members = props.gang.members.filter((member: GangMember) => + member.name.indexOf(filter) > -1 || member.task.indexOf(filter) > -1); return (<> - - Manage Equipment + setRerender(old => !old)} + gang={props.gang} /> +
+ + Manage Equipment
    - {members().map((member: GangMember) =>
  • - {member.name}} - panelContent={} /> + {members.map((member: GangMember) =>
  • +
  • )}
); diff --git a/src/Gang/ui/Panel1.tsx b/src/Gang/ui/GangMemberStats.tsx similarity index 56% rename from src/Gang/ui/Panel1.tsx rename to src/Gang/ui/GangMemberStats.tsx index dcb6d5601..a47eda0da 100644 --- a/src/Gang/ui/Panel1.tsx +++ b/src/Gang/ui/GangMemberStats.tsx @@ -1,59 +1,21 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { dialogBoxCreate } from "../../../utils/DialogBox"; import { formatNumber } from "../../../utils/StringHelperFunctions"; import { numeralWrapper } from "../../ui/numeralFormat"; import { createPopup, removePopup } from "../../ui/React/createPopup"; import { Gang } from "../Gang"; import { GangMember } from "../GangMember"; - -interface IAscendProps { - member: GangMember; - gang: Gang; - popupId: string; -} - -function ascendPopup(props: IAscendProps): React.ReactElement { - function confirm(): void { - props.gang.ascendMember(props.member); - removePopup(props.popupId); - } - - function cancel(): void { - removePopup(props.popupId); - } - - const ascendBenefits = props.member.getAscensionResults(); - - return (<> -
-Are you sure you want to ascend this member? They will lose all of
-their non-Augmentation upgrades and their stats will reset back to 1.
-
-Furthermore, your gang will lose {numeralWrapper.formatRespect(props.member.earnedRespect)} respect
-
-In return, they will gain the following permanent boost to stat multipliers:
-Hacking: +{numeralWrapper.formatPercentage(ascendBenefits.hack/100)}
-Strength: +{numeralWrapper.formatPercentage(ascendBenefits.str/100)}
-Defense: +{numeralWrapper.formatPercentage(ascendBenefits.def/100)}
-Dexterity: +{numeralWrapper.formatPercentage(ascendBenefits.dex/100)}
-Agility: +{numeralWrapper.formatPercentage(ascendBenefits.agi/100)}
-Charisma: +{numeralWrapper.formatPercentage(ascendBenefits.cha/100)}
-
- - - ); -} +import { AscensionPopup } from "./AscensionPopup"; interface IProps { member: GangMember; gang: Gang; } -export function Panel1(props: IProps): React.ReactElement { - +export function GangMemberStats(props: IProps): React.ReactElement { function ascend(): void { const popupId = `gang-management-ascend-member ${props.member.name}`; - createPopup(popupId, ascendPopup, { + createPopup(popupId, AscensionPopup, { member: props.member, gang: props.gang, popupId: popupId, @@ -85,12 +47,12 @@ Ag: x{numeralWrapper.formatMultiplier(props.member.agi_mult * props.member.agi_a Ch: x{numeralWrapper.formatMultiplier(props.member.cha_mult * props.member.cha_asc_mult)}(x{numeralWrapper.formatMultiplier(props.member.cha_mult)} Eq, x{numeralWrapper.formatMultiplier(props.member.cha_asc_mult)} Asc)
-        Hacking: {formatNumber(props.member.hack, 0)} ({numeralWrapper.formatExp(props.member.hack_exp)} exp)
- Strength: {formatNumber(props.member.str, 0)} ({numeralWrapper.formatExp(props.member.str_exp)} exp)
- Defense: {formatNumber(props.member.def, 0)} ({numeralWrapper.formatExp(props.member.def_exp)} exp)
- Dexterity: {formatNumber(props.member.dex, 0)} ({numeralWrapper.formatExp(props.member.dex_exp)} exp)
- Agility: {formatNumber(props.member.agi, 0)} ({numeralWrapper.formatExp(props.member.agi_exp)} exp)
- Charisma: {formatNumber(props.member.cha, 0)} ({numeralWrapper.formatExp(props.member.cha_exp)} exp)
+Hacking: {formatNumber(props.member.hack, 0)} ({numeralWrapper.formatExp(props.member.hack_exp)} exp)
+Strength: {formatNumber(props.member.str, 0)} ({numeralWrapper.formatExp(props.member.str_exp)} exp)
+Defense: {formatNumber(props.member.def, 0)} ({numeralWrapper.formatExp(props.member.def_exp)} exp)
+Dexterity: {formatNumber(props.member.dex, 0)} ({numeralWrapper.formatExp(props.member.dex_exp)} exp)
+Agility: {formatNumber(props.member.agi, 0)} ({numeralWrapper.formatExp(props.member.agi_exp)} exp)
+Charisma: {formatNumber(props.member.cha, 0)} ({numeralWrapper.formatExp(props.member.cha_exp)} exp)

diff --git a/src/Gang/ui/GangMemberUpgradePopup.tsx b/src/Gang/ui/GangMemberUpgradePopup.tsx index bd7b80128..a1ae8a9d0 100644 --- a/src/Gang/ui/GangMemberUpgradePopup.tsx +++ b/src/Gang/ui/GangMemberUpgradePopup.tsx @@ -8,6 +8,7 @@ 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 IPanelProps { member: GangMember; @@ -17,49 +18,31 @@ interface IPanelProps { function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement { const setRerender = useState(false)[1]; - // Upgrade buttons. Only show upgrades that can be afforded - const weaponUpgrades: GangMemberUpgrade[] = []; - const armorUpgrades: GangMemberUpgrade[] = []; - const vehicleUpgrades: GangMemberUpgrade[] = []; - const rootkitUpgrades: GangMemberUpgrade[] = []; - const augUpgrades: GangMemberUpgrade[] = []; - - for (const upgName in GangMemberUpgrades) { - if (GangMemberUpgrades.hasOwnProperty(upgName)) { + 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))) continue; - if (props.member.upgrades.includes(upgName) || props.member.augmentations.includes(upgName)) continue; - switch (upg.type) { - case "w": - weaponUpgrades.push(upg); - break; - case "a": - armorUpgrades.push(upg); - break; - case "v": - vehicleUpgrades.push(upg); - break; - case "r": - rootkitUpgrades.push(upg); - break; - case "g": - augUpgrades.push(upg); - break; - default: - console.error(`ERROR: Invalid Gang Member Upgrade Type: ${upg.type}`); - } - } + 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 purchased(name: string): React.ReactElement { - const upg = GangMemberUpgrades[name] - return (
+ function purchased(upgName: string): React.ReactElement { + const upg = GangMemberUpgrades[upgName] + return (
{upg.name}
); } - function upgradeButton(upg: GangMemberUpgrade, left = false): React.ReactElement { + function upgradeButton(upg: GangMemberUpgrade, left: boolean = false): React.ReactElement { function onClick(): void { props.member.buyUpgrade(upg, props.player, props.gang); setRerender(old => !old); @@ -132,11 +115,25 @@ export function GangMemberUpgradePopup(props: IProps): React.ReactElement { }, []); return (<> - setFilter(event.target.value)} /> + setFilter(event.target.value)} />

Discount: -{numeralWrapper.formatPercentage(1 - 1 / props.gang.getDiscount())} - You get a discount on equipment and upgrades based on your gang's respect and power. More respect and power leads to more discounts. + + You get a discount on equipment and upgrades based on your + gang's respect and power. More respect and power leads to more + discounts. +

- {props.gang.members.map((member: GangMember) => )} + {props.gang.members.map((member: GangMember) => + ) + } ); } diff --git a/src/Gang/ui/GangStats.tsx b/src/Gang/ui/GangStats.tsx index e3d96db23..1037ceff0 100644 --- a/src/Gang/ui/GangStats.tsx +++ b/src/Gang/ui/GangStats.tsx @@ -1,125 +1,18 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { Factions } from "../../Faction/Factions"; import { Gang } from "../Gang"; -import { - formatNumber, - convertTimeMsToTimeElapsedString, -} from "../../../utils/StringHelperFunctions"; +import { formatNumber } from "../../../utils/StringHelperFunctions"; import { numeralWrapper } from "../../ui/numeralFormat"; import { MoneyRate } from "../../ui/React/MoneyRate"; import { Reputation } from "../../ui/React/Reputation"; import { AllGangs } from "../AllGangs"; -import { GangConstants } from "../data/Constants"; -import { createPopup, removePopup } from "../../ui/React/createPopup"; -import { dialogBoxCreate } from "../../../utils/DialogBox"; - -interface IRecruitPopupProps { - gang: Gang; - popupId: string; -} - -function recruitPopup(props: IRecruitPopupProps): React.ReactElement { - const [name, setName] = useState(""); - - function recruit(): void { - if (name === "") { - dialogBoxCreate("You must enter a name for your Gang member!"); - return; - } - if (!props.gang.canRecruitMember()) { - dialogBoxCreate("You cannot recruit another Gang member!"); - return; - } - - // At this point, the only way this can fail is if you already - // have a gang member with the same name - if (!props.gang.recruitMember(name)) { - dialogBoxCreate("You already have a gang member with this name!"); - return; - } - - removePopup(props.popupId); - } - - function cancel(): void { - removePopup(props.popupId); - } - - function onKeyUp(event: React.KeyboardEvent): void { - if(event.keyCode === 13) recruit(); - if(event.keyCode === 27) cancel(); - } - - function onChange(event: React.ChangeEvent): void { - setName(event.target.value); - } - - return (<> -

Enter a name for your new Gang member:


- - Recruit Gang Member - Cancel - ); -} +import { BonusTime } from "./BonusTime"; interface IProps { gang: Gang; } -function Recruitment(props: IProps): React.ReactElement { - // Toggle the 'Recruit member button' if valid - const numMembers = props.gang.members.length; - const respectCost = props.gang.getRespectNeededToRecruitMember(); - - if (numMembers >= GangConstants.MaximumGangMembers) { - return (<>); - } else if (props.gang.canRecruitMember()) { - function onClick(): void { - const popupId = "recruit-gang-member-popup"; - createPopup(popupId, recruitPopup, { - gang: props.gang, - popupId: popupId, - }); - } - return (<> - - Recruit Gang Member - - ); - } - return (<> - - Recruit Gang Member - -

- {formatNumber(respectCost, 2)} respect needed to recruit next member -

- ); -} - -function BonusTime(props: IProps): React.ReactElement { - const CyclesPerSecond = 1000 / 200; - if (props.gang.storedCycles / CyclesPerSecond*1000 <= 5000) return <>; - return (<> -

- Bonus time: {convertTimeMsToTimeElapsedString(props.gang.storedCycles / CyclesPerSecond*1000)} - - 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 - -

-
- ); -} - export function GangStats(props: IProps): React.ReactElement { const territoryMult = AllGangs[props.gang.facName].territory * 100; let territoryStr; @@ -171,7 +64,5 @@ export function GangStats(props: IProps): React.ReactElement {


-
- ); } \ No newline at end of file diff --git a/src/Gang/ui/ManagementSubpage.tsx b/src/Gang/ui/ManagementSubpage.tsx index b79c0979a..8ea57ad11 100644 --- a/src/Gang/ui/ManagementSubpage.tsx +++ b/src/Gang/ui/ManagementSubpage.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { GangStats } from "./GangStats"; import { Gang } from "../Gang"; @@ -12,24 +12,28 @@ interface IProps { export function ManagementSubpage(props: IProps): React.ReactElement { return (

- 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.

- If a gang member is not earning much money or respect, the task that you - have assigned to that member might be too difficult. Consider training that - member's stats or choosing an easier task. The tasks closer to the - top of the dropdown list are generally easier. Alternatively, the gang member's - low production might be due to the fact that your wanted level is too high. - Consider assigning a few members to the '{props.gang.isHackingGang?"Ethical Hacking":"Vigilante Justice"}' + If a gang member is not earning much money or respect, the task that + you have assigned to that member might be too difficult. Consider + training that member's stats or choosing an easier task. The tasks + closer to the top of the dropdown list are generally easier. + Alternatively, the gang member's low production might be due to the + fact that your wanted level is too high. Consider assigning a few + members to the '{props.gang.isHackingGang?"Ethical Hacking":"Vigilante Justice"}' task to lower your wanted level.

- Installing Augmentations does NOT reset your progress with your Gang. - Furthermore, after installing Augmentations, you will - automatically be a member of whatever Faction you created your gang with. + Installing Augmentations does NOT reset your progress with your + Gang. Furthermore, after installing Augmentations, you will + automatically be a member of whatever Faction you created your gang + with.

- 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


diff --git a/src/Gang/ui/RecruitButton.tsx b/src/Gang/ui/RecruitButton.tsx new file mode 100644 index 000000000..0f9b1c903 --- /dev/null +++ b/src/Gang/ui/RecruitButton.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import { Gang } from "../Gang"; +import { RecruitPopup } from "./RecruitPopup"; +import { GangConstants } from "../data/Constants"; +import { formatNumber } from "../../../utils/StringHelperFunctions"; +import { createPopup } from "../../ui/React/createPopup"; + +interface IProps { + gang: Gang; + onRecruit: () => void; +} + +export function RecruitButton(props: IProps): React.ReactElement { + if (props.gang.members.length >= GangConstants.MaximumGangMembers) { + return (<>); + } + + if (!props.gang.canRecruitMember()) { + const respect = props.gang.getRespectNeededToRecruitMember(); + return (<> + + Recruit Gang Member + +

+ {formatNumber(respect, 2)} respect needed to recruit next member +

+ ); + } + + function onClick(): void { + const popupId = "recruit-gang-member-popup"; + createPopup(popupId, RecruitPopup, { + gang: props.gang, + popupId: popupId, + onRecruit: props.onRecruit, + }); + } + + return (<> + + Recruit Gang Member + + ); +} \ No newline at end of file diff --git a/src/Gang/ui/RecruitPopup.tsx b/src/Gang/ui/RecruitPopup.tsx new file mode 100644 index 000000000..ba328e5c3 --- /dev/null +++ b/src/Gang/ui/RecruitPopup.tsx @@ -0,0 +1,60 @@ +import React, { useState } from "react"; +import { Gang } from "../Gang"; +import { removePopup } from "../../ui/React/createPopup"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; + +interface IRecruitPopupProps { + gang: Gang; + popupId: string; + onRecruit: () => void; +} + +export function RecruitPopup(props: IRecruitPopupProps): React.ReactElement { + const [name, setName] = useState(""); + + function recruit(): void { + if (name === "") { + dialogBoxCreate("You must enter a name for your Gang member!"); + return; + } + if (!props.gang.canRecruitMember()) { + dialogBoxCreate("You cannot recruit another Gang member!"); + return; + } + + // At this point, the only way this can fail is if you already + // have a gang member with the same name + if (!props.gang.recruitMember(name)) { + dialogBoxCreate("You already have a gang member with this name!"); + return; + } + + props.onRecruit(); + removePopup(props.popupId); + } + + function cancel(): void { + removePopup(props.popupId); + } + + function onKeyUp(event: React.KeyboardEvent): void { + if(event.keyCode === 13) recruit(); + if(event.keyCode === 27) cancel(); + } + + function onChange(event: React.ChangeEvent): void { + setName(event.target.value); + } + + return (<> +

Enter a name for your new Gang member:


+ + Recruit Gang Member + Cancel + ); +} \ No newline at end of file diff --git a/src/Gang/ui/Root.tsx b/src/Gang/ui/Root.tsx index 2859b41cf..906bf9acc 100644 --- a/src/Gang/ui/Root.tsx +++ b/src/Gang/ui/Root.tsx @@ -27,9 +27,18 @@ export function Root(props: IProps): React.ReactElement { } return (<> - Back - setManagement(true)}>Gang Management - setManagement(false)}>Gang Territory + Back + setManagement(true)}> + Gang Management + + setManagement(false)}> + Gang Territory + {management ? : } diff --git a/src/Gang/ui/Panel3.tsx b/src/Gang/ui/TaskDescription.tsx similarity index 80% rename from src/Gang/ui/Panel3.tsx rename to src/Gang/ui/TaskDescription.tsx index b7e33e742..82715b17a 100644 --- a/src/Gang/ui/Panel3.tsx +++ b/src/Gang/ui/TaskDescription.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { GangMemberTasks } from "../GangMemberTasks"; import { GangMember } from "../GangMember"; @@ -6,7 +6,7 @@ interface IProps { member: GangMember; } -export function Panel3(props: IProps): React.ReactElement { +export function TaskDescription(props: IProps): React.ReactElement { const task = GangMemberTasks[props.member.task]; const desc = task ? task.desc: GangMemberTasks["Unassigned"].desc; diff --git a/src/Gang/ui/Panel2.tsx b/src/Gang/ui/TaskSelector.tsx similarity index 93% rename from src/Gang/ui/Panel2.tsx rename to src/Gang/ui/TaskSelector.tsx index d87e1f73c..105e21dc7 100644 --- a/src/Gang/ui/Panel2.tsx +++ b/src/Gang/ui/TaskSelector.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState } from "react"; import { numeralWrapper } from "../../ui/numeralFormat"; import { StatsTable } from "../../ui/React/StatsTable"; import { MoneyRate } from "../../ui/React/MoneyRate"; @@ -11,7 +11,7 @@ interface IProps { onTaskChange: () => void; } -export function Panel2(props: IProps): React.ReactElement { +export function TaskSelector(props: IProps): React.ReactElement { const [currentTask, setCurrentTask] = useState(props.member.task); function onChange(event: React.ChangeEvent): void { diff --git a/src/Gang/ui/TerritorySubpage.tsx b/src/Gang/ui/TerritorySubpage.tsx index f6af15d30..d5ea8df91 100644 --- a/src/Gang/ui/TerritorySubpage.tsx +++ b/src/Gang/ui/TerritorySubpage.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { numeralWrapper } from "../../ui/numeralFormat"; import { dialogBoxCreate } from "../../../utils/DialogBox"; import { formatNumber } from "../../../utils/StringHelperFunctions"; @@ -11,13 +11,13 @@ interface IProps { export function TerritorySubpage(props: IProps): React.ReactElement { function openWarfareHelp(): void { - dialogBoxCreate("This percentage represents the chance you have of 'clashing' with " + - "with another gang. If you do not wish to gain/lose territory, " + - "then keep this percentage at 0% by not engaging in territory " + - "warfare.") + dialogBoxCreate("This percentage represents the chance you have of " + + "'clashing' with with another gang. If you do not " + + "wish to gain/lose territory, then keep this " + + "percentage at 0% by not engaging in territory warfare."); } - function formatTerritoryP(n: number): string { + function formatTerritory(n: number): string { const v = n * 100; if (v <= 0) { return formatNumber(0, 2); @@ -35,7 +35,7 @@ export function TerritorySubpage(props: IProps): React.ReactElement { return ( {name}
Power: {formatNumber(power, 6)}
- Territory: {formatTerritoryP(AllGangs[name].territory)}%
+ Territory: {formatTerritory(AllGangs[name].territory)}%
Chance to win clash with this gang: {numeralWrapper.formatPercentage(clashVictoryChance, 3)}

); @@ -72,8 +72,17 @@ export function TerritorySubpage(props: IProps): React.ReactElement {

- props.gang.territoryWarfareEngaged = event.target.checked}/> -