TYPESAFETY: FactionName (#644)

This commit is contained in:
Snarling
2023-06-25 22:53:35 -04:00
committed by GitHub
parent 1de676972f
commit 9a0a843ffc
31 changed files with 295 additions and 751 deletions
+1 -8
View File
@@ -23,7 +23,7 @@ import { BladeburnerConstants } from "./data/Constants";
import { formatExp, formatMoney, formatPercent, formatBigNumber, formatStamina } from "../ui/formatNumber"; import { formatExp, formatMoney, formatPercent, formatBigNumber, formatStamina } from "../ui/formatNumber";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { addOffset } from "../utils/helpers/addOffset"; import { addOffset } from "../utils/helpers/addOffset";
import { Factions, factionExists } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital"; import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
@@ -1608,19 +1608,12 @@ export class Bladeburner {
this.maxRank = Math.max(this.rank, this.maxRank); this.maxRank = Math.max(this.rank, this.maxRank);
const bladeburnersFactionName = FactionName.Bladeburners; const bladeburnersFactionName = FactionName.Bladeburners;
if (factionExists(bladeburnersFactionName)) {
const bladeburnerFac = Factions[bladeburnersFactionName]; const bladeburnerFac = Factions[bladeburnersFactionName];
if (!bladeburnerFac) {
throw new Error(
`Could not properly get ${FactionName.Bladeburners} Faction object in ${FactionName.Bladeburners} UI Overview Faction button`,
);
}
if (bladeburnerFac.isMember) { if (bladeburnerFac.isMember) {
const favorBonus = 1 + bladeburnerFac.favor / 100; const favorBonus = 1 + bladeburnerFac.favor / 100;
bladeburnerFac.playerReputation += bladeburnerFac.playerReputation +=
BladeburnerConstants.RankToFactionRepFactor * change * person.mults.faction_rep * favorBonus; BladeburnerConstants.RankToFactionRepFactor * change * person.mults.faction_rep * favorBonus;
} }
}
// Gain skill points // Gain skill points
const rankNeededForSp = (this.totalSkillPoints + 1) * BladeburnerConstants.RanksPerSkillPoint; const rankNeededForSp = (this.totalSkillPoints + 1) * BladeburnerConstants.RanksPerSkillPoint;
+8 -17
View File
@@ -124,38 +124,29 @@ function getRandomProblemType(): string {
} }
function getRandomReward(): ICodingContractReward { function getRandomReward(): ICodingContractReward {
const reward: ICodingContractReward = { const rewardType = sanitizeRewardType(getRandomInt(0, CodingContractRewardType.Money));
name: "",
type: getRandomInt(0, CodingContractRewardType.Money),
};
reward.type = sanitizeRewardType(reward.type);
// Add additional information based on the reward type // Add additional information based on the reward type
const factionsThatAllowHacking = Player.factions.filter((fac) => Factions[fac].getInfo().offerHackingWork); const factionsThatAllowHacking = Player.factions.filter((fac) => Factions[fac].getInfo().offerHackingWork);
switch (reward.type) { switch (rewardType) {
case CodingContractRewardType.FactionReputation: { case CodingContractRewardType.FactionReputation: {
// Get a random faction that player is a part of. That // Get a random faction that player is a part of. That
// faction must allow hacking contracts // faction must allow hacking contracts
const numFactions = factionsThatAllowHacking.length; const numFactions = factionsThatAllowHacking.length;
const randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)]; const randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)];
reward.name = randFaction; return { type: rewardType, name: randFaction };
break;
} }
case CodingContractRewardType.CompanyReputation: { case CodingContractRewardType.CompanyReputation: {
const allJobs = Object.keys(Player.jobs); const allJobs = Object.keys(Player.jobs);
if (allJobs.length > 0) { if (allJobs.length > 0) {
reward.name = allJobs[getRandomInt(0, allJobs.length - 1)]; return { type: CodingContractRewardType.CompanyReputation, name: allJobs[getRandomInt(0, allJobs.length - 1)] };
} else {
reward.type = CodingContractRewardType.Money;
} }
break; return { type: CodingContractRewardType.Money };
} }
default: default:
break; return { type: rewardType };
} }
return reward;
} }
function getRandomServer(): BaseServer { function getRandomServer(): BaseServer {
@@ -182,7 +173,7 @@ function getRandomServer(): BaseServer {
function getRandomFilename( function getRandomFilename(
server: BaseServer, server: BaseServer,
reward: ICodingContractReward = { name: "", type: 0 }, reward: ICodingContractReward = { type: CodingContractRewardType.Money },
): ContractFilePath { ): ContractFilePath {
let contractFn = `contract-${getRandomInt(0, 1e6)}`; let contractFn = `contract-${getRandomInt(0, 1e6)}`;
@@ -197,7 +188,7 @@ function getRandomFilename(
contractFn = `contract-${getRandomInt(0, 1e6)}`; contractFn = `contract-${getRandomInt(0, 1e6)}`;
} }
if (reward.name) { if ("name" in reward) {
// Only alphanumeric characters in the reward name. // Only alphanumeric characters in the reward name.
contractFn += `-${reward.name.replace(/[^a-zA-Z0-9]/g, "")}`; contractFn += `-${reward.name.replace(/[^a-zA-Z0-9]/g, "")}`;
} }
+16 -5
View File
@@ -1,3 +1,4 @@
import type { FactionName } from "@enums";
import { codingContractTypesMetadata, DescriptionFunc, GeneratorFunc, SolverFunc } from "./data/codingcontracttypes"; import { codingContractTypesMetadata, DescriptionFunc, GeneratorFunc, SolverFunc } from "./data/codingcontracttypes";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "./utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "./utils/JSONReviver";
@@ -73,11 +74,21 @@ export enum CodingContractResult {
} }
/** A class that represents the type of reward a contract gives */ /** A class that represents the type of reward a contract gives */
export interface ICodingContractReward { export type ICodingContractReward =
/* Name of Company/Faction name for reward, if applicable */ | {
name?: string; type: CodingContractRewardType.Money;
type: CodingContractRewardType; }
} | {
type: CodingContractRewardType.FactionReputationAll;
}
| {
type: CodingContractRewardType.CompanyReputation;
name: string;
}
| {
type: CodingContractRewardType.FactionReputation;
name: FactionName;
};
/** /**
* A Coding Contract is a file that poses a programming-related problem to the Player. * A Coding Contract is a file that poses a programming-related problem to the Player.
@@ -1,17 +1,17 @@
import type { FactionName } from "@enums";
import React, { useState } from "react"; import React, { useState } from "react";
import { Box, Button, MenuItem, Select, SelectChangeEvent, Typography } from "@mui/material";
import { Player } from "@player";
import { Factions } from "../../../Faction/Factions"; import { Factions } from "../../../Faction/Factions";
import * as corpConstants from "../../data/Constants"; import * as corpConstants from "../../data/Constants";
import { formatReputation } from "../../../ui/formatNumber"; import { formatReputation } from "../../../ui/formatNumber";
import { dialogBoxCreate } from "../../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { Player } from "@player";
import { useCorporation } from "../Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
import { NumberInput } from "../../../ui/React/NumberInput"; import { NumberInput } from "../../../ui/React/NumberInput";
import Box from "@mui/material/Box"; import { getEnumHelper } from "../../../utils/EnumHelper";
import Select, { SelectChangeEvent } from "@mui/material/Select";
interface IProps { interface IProps {
open: boolean; open: boolean;
@@ -19,7 +19,7 @@ interface IProps {
} }
export function BribeFactionModal(props: IProps): React.ReactElement { export function BribeFactionModal(props: IProps): React.ReactElement {
const factions = Player.factions.filter((name: string) => { const factions = Player.factions.filter((name) => {
const info = Factions[name].getInfo(); const info = Factions[name].getInfo();
if (!info.offersWork()) return false; if (!info.offersWork()) return false;
if (Player.hasGangWith(name)) return false; if (Player.hasGangWith(name)) return false;
@@ -27,10 +27,11 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
}); });
const corp = useCorporation(); const corp = useCorporation();
const [money, setMoney] = useState<number>(NaN); const [money, setMoney] = useState<number>(NaN);
const [selectedFaction, setSelectedFaction] = useState(factions.length > 0 ? factions[0] : ""); const [selectedFaction, setSelectedFaction] = useState<FactionName | "">(factions.length > 0 ? factions[0] : "");
const disabled = money === 0 || isNaN(money) || money < 0 || corp.funds < money; const disabled = money === 0 || isNaN(money) || money < 0 || corp.funds < money;
function changeFaction(event: SelectChangeEvent): void { function changeFaction(event: SelectChangeEvent): void {
if (!getEnumHelper("FactionName").isMember(event.target.value)) return;
setSelectedFaction(event.target.value); setSelectedFaction(event.target.value);
} }
@@ -52,6 +53,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
} }
function bribe(money: number): void { function bribe(money: number): void {
if (!selectedFaction) return;
const fac = Factions[selectedFaction]; const fac = Factions[selectedFaction];
if (disabled) return; if (disabled) return;
const rep = repGain(money); const rep = repGain(money);
@@ -69,7 +71,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<Typography>Faction:</Typography> <Typography>Faction:</Typography>
<Select value={selectedFaction} onChange={changeFaction}> <Select value={selectedFaction} onChange={changeFaction}>
{factions.map((name: string) => { {factions.map((name) => {
const info = Factions[name].getInfo(); const info = Factions[name].getInfo();
if (!info.offersWork()) return; if (!info.offersWork()) return;
if (Player.hasGangWith(name)) return; if (Player.hasGangWith(name)) return;
+2 -2
View File
@@ -5,7 +5,7 @@ import React, { useEffect } from "react";
import { General } from "./DevMenu/ui/General"; import { General } from "./DevMenu/ui/General";
import { Stats } from "./DevMenu/ui/Stats"; import { Stats } from "./DevMenu/ui/Stats";
import { Factions } from "./DevMenu/ui/Factions"; import { FactionsDev } from "./DevMenu/ui/FactionsDev";
import { Augmentations } from "./DevMenu/ui/Augmentations"; import { Augmentations } from "./DevMenu/ui/Augmentations";
import { SourceFiles } from "./DevMenu/ui/SourceFiles"; import { SourceFiles } from "./DevMenu/ui/SourceFiles";
import { Programs } from "./DevMenu/ui/Programs"; import { Programs } from "./DevMenu/ui/Programs";
@@ -34,7 +34,7 @@ export function DevMenuRoot(): React.ReactElement {
<Typography>Development Menu - Only meant to be used for testing/debugging</Typography> <Typography>Development Menu - Only meant to be used for testing/debugging</Typography>
<General /> <General />
<Stats /> <Stats />
<Factions /> <FactionsDev />
<Augmentations /> <Augmentations />
<SourceFiles /> <SourceFiles />
<Programs /> <Programs />
@@ -1,7 +1,4 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Player } from "@player";
import { FactionName } from "@enums";
import { import {
Accordion, Accordion,
AccordionSummary, AccordionSummary,
@@ -11,27 +8,33 @@ import {
IconButton, IconButton,
InputLabel, InputLabel,
MenuItem, MenuItem,
Select,
SelectChangeEvent,
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { Adjuster } from "./Adjuster";
import { Factions as AllFaction } from "../../Faction/Factions";
import ReplyAllIcon from "@mui/icons-material/ReplyAll"; import ReplyAllIcon from "@mui/icons-material/ReplyAll";
import ReplyIcon from "@mui/icons-material/Reply"; import ReplyIcon from "@mui/icons-material/Reply";
import { Player } from "@player";
import { FactionName } from "@enums";
import { Adjuster } from "./Adjuster";
import { Factions } from "../../Faction/Factions";
import { getRecordValues } from "../../Types/Record";
import { getEnumHelper } from "../../utils/EnumHelper";
const bigNumber = 1e12; const bigNumber = 1e12;
export function Factions(): React.ReactElement { export function FactionsDev(): React.ReactElement {
const [faction, setFaction] = useState(FactionName.Illuminati as string); const [factionName, setFactionName] = useState(FactionName.Illuminati);
function setFactionDropdown(event: SelectChangeEvent): void { function setFactionDropdown(event: SelectChangeEvent): void {
setFaction(event.target.value); if (!getEnumHelper("FactionName").isMember(event.target.value)) return;
setFactionName(event.target.value);
} }
function receiveInvite(): void { function receiveInvite(): void {
Player.receiveInvite(faction); Player.receiveInvite(factionName);
} }
function receiveAllInvites(): void { function receiveAllInvites(): void {
@@ -40,57 +43,53 @@ export function Factions(): React.ReactElement {
function modifyFactionRep(modifier: number): (x: number) => void { function modifyFactionRep(modifier: number): (x: number) => void {
return function (reputation: number): void { return function (reputation: number): void {
const fac = AllFaction[faction]; const fac = Factions[factionName];
if (fac != null && !isNaN(reputation)) { if (!isNaN(reputation)) {
fac.playerReputation += reputation * modifier; fac.playerReputation += reputation * modifier;
} }
}; };
} }
function resetFactionRep(): void { function resetFactionRep(): void {
const fac = AllFaction[faction]; const fac = Factions[factionName];
if (fac != null) {
fac.playerReputation = 0; fac.playerReputation = 0;
} }
}
function modifyFactionFavor(modifier: number): (x: number) => void { function modifyFactionFavor(modifier: number): (x: number) => void {
return function (favor: number): void { return function (favor: number): void {
const fac = AllFaction[faction]; const fac = Factions[factionName];
if (fac != null && !isNaN(favor)) { if (!isNaN(favor)) {
fac.favor += favor * modifier; fac.favor += favor * modifier;
} }
}; };
} }
function resetFactionFavor(): void { function resetFactionFavor(): void {
const fac = AllFaction[faction]; const fac = Factions[factionName];
if (fac != null) {
fac.favor = 0; fac.favor = 0;
} }
}
function tonsOfRep(): void { function tonsOfRep(): void {
for (const i of Object.keys(AllFaction)) { for (const faction of getRecordValues(Factions)) {
AllFaction[i].playerReputation = bigNumber; faction.playerReputation = bigNumber;
} }
} }
function resetAllRep(): void { function resetAllRep(): void {
for (const i of Object.keys(AllFaction)) { for (const faction of getRecordValues(Factions)) {
AllFaction[i].playerReputation = 0; faction.playerReputation = 0;
} }
} }
function tonsOfFactionFavor(): void { function tonsOfFactionFavor(): void {
for (const i of Object.keys(AllFaction)) { for (const faction of getRecordValues(Factions)) {
AllFaction[i].favor = bigNumber; faction.favor = bigNumber;
} }
} }
function resetAllFactionFavor(): void { function resetAllFactionFavor(): void {
for (const i of Object.keys(AllFaction)) { for (const faction of getRecordValues(Factions)) {
AllFaction[i].favor = 0; faction.favor = 0;
} }
} }
@@ -113,7 +112,7 @@ export function Factions(): React.ReactElement {
labelId="factions-select" labelId="factions-select"
id="factions-dropdown" id="factions-dropdown"
onChange={setFactionDropdown} onChange={setFactionDropdown}
value={faction} value={factionName}
startAdornment={ startAdornment={
<> <>
<IconButton onClick={receiveAllInvites} size="large" arial-label="receive-all-invitation"> <IconButton onClick={receiveAllInvites} size="large" arial-label="receive-all-invitation">
@@ -125,7 +124,7 @@ export function Factions(): React.ReactElement {
</> </>
} }
> >
{Object.values(AllFaction).map((faction) => ( {Object.values(Factions).map((faction) => (
<MenuItem key={faction.name} value={faction.name}> <MenuItem key={faction.name} value={faction.name}>
{faction.name} {faction.name}
</MenuItem> </MenuItem>
+21 -10
View File
@@ -1,24 +1,31 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import Accordion from "@mui/material/Accordion"; import {
import AccordionSummary from "@mui/material/AccordionSummary"; Accordion,
import AccordionDetails from "@mui/material/AccordionDetails"; AccordionSummary,
AccordionDetails,
Button,
MenuItem,
Select,
SelectChangeEvent,
TextField,
Typography,
} from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import { Money } from "../../ui/React/Money";
import { Player } from "@player"; import { Player } from "@player";
import { FactionName } from "@enums";
import { Money } from "../../ui/React/Money";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { MenuItem, SelectChangeEvent, TextField, Select } from "@mui/material";
import { Bladeburner } from "../../Bladeburner/Bladeburner"; import { Bladeburner } from "../../Bladeburner/Bladeburner";
import { GangConstants } from "../../Gang/data/Constants"; import { GangConstants } from "../../Gang/data/Constants";
import { FactionName } from "@enums";
import { checkForMessagesToSend } from "../../Message/MessageHelpers"; import { checkForMessagesToSend } from "../../Message/MessageHelpers";
import { ThemeEvents } from "../../Themes/ui/Theme"; import { ThemeEvents } from "../../Themes/ui/Theme";
import { getEnumHelper } from "../../utils/EnumHelper";
export function General(): React.ReactElement { export function General(): React.ReactElement {
const [error, setError] = useState(false); const [error, setError] = useState(false);
const [corporationName, setCorporationName] = useState(""); const [corporationName, setCorporationName] = useState("");
const [gangFaction, setGangFaction] = useState("Slum Snakes"); const [gangFaction, setGangFaction] = useState(FactionName.SlumSnakes);
const [devMoney, setDevMoney] = useState(0); const [devMoney, setDevMoney] = useState(0);
// Money functions // Money functions
@@ -71,7 +78,11 @@ export function General(): React.ReactElement {
// Rerender so the gang menu option will be removed immediately on the devmenu page selection // Rerender so the gang menu option will be removed immediately on the devmenu page selection
ThemeEvents.emit(); ThemeEvents.emit();
}; };
const setGangFactionDropdown = (event: SelectChangeEvent) => setGangFaction(event.target.value); const setGangFactionDropdown = (event: SelectChangeEvent) => {
// Todo: Make this a more specific check when a GangName enumlike is added
if (!getEnumHelper("FactionName").isMember(event.target.value)) return;
setGangFaction(event.target.value);
};
// Misc functions // Misc functions
const checkMessages = () => checkForMessagesToSend(); const checkMessages = () => checkForMessagesToSend();
+23 -15
View File
@@ -1,8 +1,8 @@
import type { AugmentationName } from "@enums"; import { AugmentationName, FactionName } from "@enums";
import { FactionInfo, FactionInfos } from "./FactionInfo"; import { FactionInfo, FactionInfos } from "./FactionInfo";
import { favorToRep, repToFavor } from "./formulas/favor"; import { favorToRep, repToFavor } from "./formulas/favor";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { getEnumHelper } from "../utils/EnumHelper"; import { getKeyList } from "../utils/helpers/getKeyList";
export class Faction { export class Faction {
/** /**
@@ -24,12 +24,12 @@ export class Faction {
isMember = false; isMember = false;
/** Name of faction */ /** Name of faction */
name = ""; name: FactionName;
/** Amount of reputation player has with this faction */ /** Amount of reputation player has with this faction */
playerReputation = 0; playerReputation = 0;
constructor(name = "") { constructor(name = FactionName.Sector12) {
this.name = name; this.name = name;
} }
@@ -44,11 +44,24 @@ export class Faction {
return info; return info;
} }
gainFavor(): void { prestigeSourceFile() {
if (this.favor == null) { // Reset favor, reputation, and flags
this.favor = 0; this.favor = 0;
this.playerReputation = 0;
this.alreadyInvited = false;
this.isMember = false;
this.isBanned = false;
} }
prestigeAugmentation(): void {
// Gain favor
if (this.favor == null) this.favor = 0;
this.favor += this.getFavorGain(); this.favor += this.getFavorGain();
// Reset reputation and flags
this.playerReputation = 0;
this.alreadyInvited = false;
this.isMember = false;
this.isBanned = false;
} }
//Returns an array with [How much favor would be gained, how much rep would be left over] //Returns an array with [How much favor would be gained, how much rep would be left over]
@@ -62,21 +75,16 @@ export class Faction {
return newFavor - this.favor; return newFavor - this.favor;
} }
static savedKeys = getKeyList(Faction, { removedKeys: ["augmentations", "name"] });
/** Serialize the current object to a JSON save state. */ /** Serialize the current object to a JSON save state. */
toJSON(): IReviverValue { toJSON(): IReviverValue {
return Generic_toJSON("Faction", this); return Generic_toJSON("Faction", this, Faction.savedKeys);
} }
/** Initializes a Faction object from a JSON save state. */ /** Initializes a Faction object from a JSON save state. */
static fromJSON(value: IReviverValue): Faction { static fromJSON(value: IReviverValue): Faction {
const faction = Generic_fromJSON(Faction, value.data); return Generic_fromJSON(Faction, value.data, Faction.savedKeys);
if (!Array.isArray(faction.augmentations)) faction.augmentations = [];
// Remove invalid augs from faction. Augs are repopulated with correct augs during any reset.
const augHelper = getEnumHelper("AugmentationName");
faction.augmentations = faction.augmentations.filter((augName) => augHelper.isMember(augName));
// Fix broken saves, this will soon be removed when better fix is implemented
faction.augmentations = [...new Set(faction.augmentations)];
return faction;
} }
} }
+9 -7
View File
@@ -1,12 +1,13 @@
import type { Augmentation } from "../Augmentation/Augmentation";
import type { Faction } from "./Faction";
import { Augmentations } from "../Augmentation/Augmentations"; import { Augmentations } from "../Augmentation/Augmentations";
import { Augmentation } from "../Augmentation/Augmentation";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationName, FactionName } from "@enums"; import { AugmentationName, FactionName } from "@enums";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Faction } from "./Faction";
import { Factions } from "./Factions";
import { Player } from "@player"; import { Player } from "@player";
import { Factions } from "./Factions";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { import {
getHackingWorkRepGain, getHackingWorkRepGain,
@@ -19,6 +20,7 @@ import { InvitationEvent } from "./ui/InvitationModal";
import { SFC32RNG } from "../Casino/RNG"; import { SFC32RNG } from "../Casino/RNG";
import { isFactionWork } from "../Work/FactionWork"; import { isFactionWork } from "../Work/FactionWork";
import { getAugCost } from "../Augmentation/AugmentationHelpers"; import { getAugCost } from "../Augmentation/AugmentationHelpers";
import { createEnumKeyedRecord, getRecordKeys } from "../Types/Record";
export function inviteToFaction(faction: Faction): void { export function inviteToFaction(faction: Faction): void {
Player.receiveInvite(faction.name); Player.receiveInvite(faction.name);
@@ -32,8 +34,9 @@ export function joinFaction(faction: Faction): void {
if (faction.isMember) return; if (faction.isMember) return;
faction.isMember = true; faction.isMember = true;
Player.factions.push(faction.name); Player.factions.push(faction.name);
const allFactions = Object.values(FactionName).map((faction) => faction as string); let i = 0;
Player.factions.sort((a, b) => allFactions.indexOf(a) - allFactions.indexOf(b)); const factionIndexes = createEnumKeyedRecord(FactionName, (__) => i++);
Player.factions.sort((a, b) => factionIndexes[a] - factionIndexes[b]);
const factionInfo = faction.getInfo(); const factionInfo = faction.getInfo();
//Determine what factions you are banned from now that you have joined this faction //Determine what factions you are banned from now that you have joined this faction
@@ -108,9 +111,8 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
export function processPassiveFactionRepGain(numCycles: number): void { export function processPassiveFactionRepGain(numCycles: number): void {
if (Player.bitNodeN === 2) return; if (Player.bitNodeN === 2) return;
for (const name of Object.keys(Factions)) { for (const name of getRecordKeys(Factions)) {
if (isFactionWork(Player.currentWork) && name === Player.currentWork.factionName) continue; if (isFactionWork(Player.currentWork) && name === Player.currentWork.factionName) continue;
if (!Object.hasOwn(Factions, name)) continue;
const faction = Factions[name]; const faction = Factions[name];
if (!faction.isMember) continue; if (!faction.isMember) continue;
// No passive rep for special factions // No passive rep for special factions
+3 -3
View File
@@ -7,7 +7,7 @@ import { Typography } from "@mui/material";
interface FactionInfoParams { interface FactionInfoParams {
infoText?: JSX.Element; infoText?: JSX.Element;
enemies?: string[]; enemies?: FactionName[];
offerHackingWork?: boolean; offerHackingWork?: boolean;
offerFieldWork?: boolean; offerFieldWork?: boolean;
offerSecurityWork?: boolean; offerSecurityWork?: boolean;
@@ -19,7 +19,7 @@ interface FactionInfoParams {
/** Contains the "information" property for all the Factions, which is just a description of each faction */ /** Contains the "information" property for all the Factions, which is just a description of each faction */
export class FactionInfo { export class FactionInfo {
/** The names of all other factions considered to be enemies to this faction. */ /** The names of all other factions considered to be enemies to this faction. */
enemies: string[]; enemies: FactionName[];
/** The descriptive text to show on the faction's page. */ /** The descriptive text to show on the faction's page. */
infoText: JSX.Element; infoText: JSX.Element;
@@ -60,7 +60,7 @@ export class FactionInfo {
} }
/** A map of all factions and associated info to them. */ /** A map of all factions and associated info to them. */
export const FactionInfos: Record<string, FactionInfo> = { export const FactionInfos: Record<FactionName, FactionInfo> = {
// Endgame // Endgame
[FactionName.Illuminati]: new FactionInfo({ [FactionName.Illuminati]: new FactionInfo({
infoText: ( infoText: (
+28 -49
View File
@@ -2,63 +2,42 @@
* Initialization and manipulation of the Factions object, which stores data * Initialization and manipulation of the Factions object, which stores data
* about all Factions in the game * about all Factions in the game
*/ */
import { FactionName } from "@enums";
import { Faction } from "./Faction"; import { Faction } from "./Faction";
import { FactionInfos } from "./FactionInfo";
import { Reviver } from "../utils/JSONReviver"; import { Reviver, assertLoadingType } from "../utils/JSONReviver";
import { getRecordValues } from "../Types/Record"; import { createEnumKeyedRecord, getRecordValues } from "../Types/Record";
import { Augmentations, initCircadianModulator } from "../Augmentation/Augmentations"; import { Augmentations } from "../Augmentation/Augmentations";
import { getEnumHelper } from "../utils/EnumHelper";
export let Factions: Record<string, Faction> = {}; /** The static list of all factions. Initialized once and never modified. */
export const Factions = createEnumKeyedRecord(FactionName, (name) => new Faction(name));
export function loadFactions(saveString: string): void { // Add the associated augs to every faction
Factions = JSON.parse(saveString, Reviver); for (const aug of getRecordValues(Augmentations)) {
// safety check for when we load older save file that don't have newer factions
for (const faction of Object.keys(Factions)) {
try {
Factions[faction].getInfo();
} catch (err) {
console.error("deleting " + faction);
delete Factions[faction];
}
}
}
function AddToFactions(faction: Faction): void {
const name: string = faction.name;
Factions[name] = faction;
}
export function factionExists(name: string): boolean {
return Object.hasOwn(Factions, name);
}
export function initFactions(): void {
for (const name of Object.keys(FactionInfos)) {
resetFaction(new Faction(name));
}
// All factions are added, this is a good place to add augs back to factions.
initCircadianModulator();
for (const aug of getRecordValues(Augmentations)) {
for (const factionName of aug.factions) { for (const factionName of aug.factions) {
const faction = Factions[factionName]; const faction = Factions[factionName];
if (!faction) {
console.error(`Faction ${factionName} did not exist while adding augs to factions`);
continue;
}
faction.augmentations.push(aug.name); faction.augmentations.push(aug.name);
} }
}
} }
//Resets a faction during (re-)initialization. Saves the favor in the new export function loadFactions(saveString: string): void {
//Faction object and deletes the old Faction Object from "Factions". Then // The only information that should be loaded from player save is
//reinserts the new Faction object const loadedFactions = JSON.parse(saveString, Reviver) as unknown;
function resetFaction(newFactionObject: Faction): void { // This loading method allows invalid data in player save, but just ignores anything invalid
const factionName: string = newFactionObject.name; if (!loadedFactions) return;
if (factionExists(factionName)) { if (typeof loadedFactions !== "object") return;
newFactionObject.favor = Factions[factionName].favor; for (const [loadedFactionName, loadedFaction] of Object.entries(loadedFactions) as [string, unknown][]) {
delete Factions[factionName]; if (!getEnumHelper("FactionName").isMember(loadedFactionName)) continue;
if (!loadedFaction) continue;
const faction = Factions[loadedFactionName];
if (typeof loadedFaction !== "object") continue;
assertLoadingType<Faction>(loadedFaction);
const { playerReputation: loadedRep, favor: loadedFavor } = loadedFaction;
if (typeof loadedRep === "number" && loadedRep > 0) faction.playerReputation = loadedRep;
if (typeof loadedFavor === "number" && loadedFavor > 0) faction.favor = loadedFavor;
// Todo, these 3 will be removed from Faction object and savedata after a separate PR changes some data structures on Player to make this unnecessary info to save
if (loadedFaction.alreadyInvited) faction.alreadyInvited = true;
if (loadedFaction.isBanned) faction.isBanned = true;
if (loadedFaction.isMember) faction.isMember = true;
} }
AddToFactions(newFactionObject);
} }
+2 -2
View File
@@ -11,7 +11,7 @@ import { FactionName } from "@enums";
interface IProps { interface IProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
facName: string; facName: FactionName;
} }
/** React Component for the popup used to create a new gang. */ /** React Component for the popup used to create a new gang. */
@@ -27,7 +27,7 @@ export function CreateGangModal(props: IProps): React.ReactElement {
"is not as important."; "is not as important.";
function isHacking(): boolean { function isHacking(): boolean {
return [FactionName.NiteSec as string, FactionName.TheBlackHand as string].includes(props.facName); return [FactionName.NiteSec, FactionName.TheBlackHand].includes(props.facName);
} }
function createGang(): void { function createGang(): void {
+4 -3
View File
@@ -1,12 +1,13 @@
import type { Faction } from "../Faction";
import React, { useEffect } from "react";
import { Explore, Info, LastPage, LocalPolice, NewReleases, Report, SportsMma } from "@mui/icons-material"; import { Explore, Info, LastPage, LocalPolice, NewReleases, Report, SportsMma } from "@mui/icons-material";
import { Box, Button, Container, Paper, Tooltip, Typography, useTheme } from "@mui/material"; import { Box, Button, Container, Paper, Tooltip, Typography, useTheme } from "@mui/material";
import React, { useEffect } from "react";
import { Player } from "@player"; import { Player } from "@player";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { formatFavor, formatReputation } from "../../ui/formatNumber"; import { formatFavor, formatReputation } from "../../ui/formatNumber";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { FactionName } from "@enums"; import { FactionName } from "@enums";
import { Faction } from "../Faction";
import { getFactionAugmentationsFiltered, joinFaction } from "../FactionHelpers"; import { getFactionAugmentationsFiltered, joinFaction } from "../FactionHelpers";
import { Factions } from "../Factions"; import { Factions } from "../Factions";
import { useRerender } from "../../ui/React/hooks"; import { useRerender } from "../../ui/React/hooks";
@@ -59,7 +60,7 @@ const FactionElement = (props: FactionElementProps): React.ReactElement => {
Router.toFaction(faction, true); Router.toFaction(faction, true);
} }
function acceptInvitation(event: React.MouseEvent<HTMLButtonElement>, faction: string): void { function acceptInvitation(event: React.MouseEvent<HTMLButtonElement>, faction: FactionName): void {
if (!event.isTrusted) return; if (!event.isTrusted) return;
joinFaction(Factions[faction]); joinFaction(Factions[faction]);
props.rerender(); props.rerender();
+3 -2
View File
@@ -23,9 +23,10 @@ import { GangMember } from "./GangMember";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { Player } from "@player"; import { Player } from "@player";
import { PowerMultiplier } from "./data/power"; import { PowerMultiplier } from "./data/power";
import { FactionName } from "@enums";
export class Gang { export class Gang {
facName: string; facName: FactionName;
members: GangMember[]; members: GangMember[];
wanted: number; wanted: number;
respect: number; respect: number;
@@ -48,7 +49,7 @@ export class Gang {
notifyMemberDeath: boolean; notifyMemberDeath: boolean;
constructor(facName = "", hacking = false) { constructor(facName = FactionName.SlumSnakes, hacking = false) {
this.facName = facName; this.facName = facName;
this.members = []; this.members = [];
this.wanted = 1; this.wanted = 1;
+10 -7
View File
@@ -1,11 +1,13 @@
import { Box, Button, MenuItem, Paper, Select, SelectChangeEvent, Typography } from "@mui/material";
import React, { useState } from "react"; import React, { useState } from "react";
import { Box, Button, MenuItem, Paper, Select, SelectChangeEvent, Typography } from "@mui/material";
import { Player } from "@player";
import { FactionName } from "@enums"; import { FactionName } from "@enums";
import { inviteToFaction } from "../../Faction/FactionHelpers"; import { inviteToFaction } from "../../Faction/FactionHelpers";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router"; import { Page } from "../../ui/Router";
import { Player } from "@player";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { formatNumberNoSuffix } from "../../ui/formatNumber"; import { formatNumberNoSuffix } from "../../ui/formatNumber";
@@ -14,6 +16,7 @@ import {
calculateSellInformationCashReward, calculateSellInformationCashReward,
calculateTradeInformationRepReward, calculateTradeInformationRepReward,
} from "../formulas/victory"; } from "../formulas/victory";
import { getEnumHelper } from "../../utils/EnumHelper";
interface IProps { interface IProps {
StartingDifficulty: number; StartingDifficulty: number;
@@ -23,7 +26,7 @@ interface IProps {
} }
export function Victory(props: IProps): React.ReactElement { export function Victory(props: IProps): React.ReactElement {
const [faction, setFaction] = useState("none"); const [factionName, setFactionName] = useState("none");
function quitInfiltration(): void { function quitInfiltration(): void {
handleInfiltrators(); handleInfiltrators();
@@ -43,13 +46,13 @@ export function Victory(props: IProps): React.ReactElement {
} }
function trade(): void { function trade(): void {
if (faction === "none") return; if (!getEnumHelper("FactionName").isMember(factionName)) return;
Factions[faction].playerReputation += repGain; Factions[factionName].playerReputation += repGain;
quitInfiltration(); quitInfiltration();
} }
function changeDropdown(event: SelectChangeEvent): void { function changeDropdown(event: SelectChangeEvent): void {
setFaction(event.target.value); setFactionName(event.target.value);
} }
function handleInfiltrators(): void { function handleInfiltrators(): void {
@@ -75,7 +78,7 @@ export function Victory(props: IProps): React.ReactElement {
</Typography> </Typography>
<Box sx={{ width: "fit-content" }}> <Box sx={{ width: "fit-content" }}>
<Box sx={{ width: "100%" }}> <Box sx={{ width: "100%" }}>
<Select value={faction} onChange={changeDropdown} sx={{ mr: 1 }}> <Select value={factionName} onChange={changeDropdown} sx={{ mr: 1 }}>
<MenuItem key={"none"} value={"none"}> <MenuItem key={"none"} value={"none"}>
{"none"} {"none"}
</MenuItem> </MenuItem>
+5 -5
View File
@@ -3,15 +3,15 @@ import { Player } from "@player";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { Faction } from "../Faction/Faction"; import { Faction } from "../Faction/Faction";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { FactionName } from "@enums"; import { AugmentationName, FactionName } from "@enums";
import { Server } from "../Server/Server"; import { Server } from "../Server/Server";
function allFactionAugs(f: Faction): boolean { function allFactionAugs(faction: Faction): boolean {
const factionAugs = f.augmentations.slice().filter((aug) => aug !== "NeuroFlux Governor"); for (const factionAugName of faction.augmentations) {
for (const factionAug of factionAugs) { if (factionAugName === AugmentationName.NeuroFluxGovernor) continue;
if ( if (
!Player.augmentations.some((aug) => { !Player.augmentations.some((aug) => {
return aug.name == factionAug; return aug.name == factionAugName;
}) })
) )
return false; return false;
+3 -4
View File
@@ -51,7 +51,7 @@ import {
} from "../Corporation/Actions"; } from "../Corporation/Actions";
import { CorpUnlocks } from "../Corporation/data/CorporationUnlocks"; import { CorpUnlocks } from "../Corporation/data/CorporationUnlocks";
import { CorpUpgrades } from "../Corporation/data/CorporationUpgrades"; import { CorpUpgrades } from "../Corporation/data/CorporationUpgrades";
import { CorpUnlockName, CorpUpgradeName, CorpEmployeeJob, CityName } from "@enums"; import { CorpUnlockName, CorpUpgradeName, CorpEmployeeJob, CityName, FactionName } from "@enums";
import { IndustriesData, IndustryResearchTrees } from "../Corporation/data/IndustryData"; import { IndustriesData, IndustryResearchTrees } from "../Corporation/data/IndustryData";
import * as corpConstants from "../Corporation/data/Constants"; import * as corpConstants from "../Corporation/data/Constants";
import { ResearchMap } from "../Corporation/ResearchMap"; import { ResearchMap } from "../Corporation/ResearchMap";
@@ -174,8 +174,7 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
return division.researched.has(researchName); return division.researched.has(researchName);
} }
function bribe(factionName: string, amountCash: number): boolean { function bribe(factionName: FactionName, amountCash: number): boolean {
if (!player.factions.includes(factionName)) throw new Error("Invalid faction name");
if (isNaN(amountCash) || amountCash < 0) if (isNaN(amountCash) || amountCash < 0)
throw new Error("Invalid value for amount field! Must be numeric, greater than 0."); throw new Error("Invalid value for amount field! Must be numeric, greater than 0.");
@@ -825,7 +824,7 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
}, },
bribe: (ctx) => (_factionName, _amountCash) => { bribe: (ctx) => (_factionName, _amountCash) => {
checkAccess(ctx); checkAccess(ctx);
const factionName = helpers.string(ctx, "factionName", _factionName); const factionName = getEnumHelper("FactionName").nsGetMember(ctx, _factionName);
const amountCash = helpers.number(ctx, "amountCash", _amountCash); const amountCash = helpers.number(ctx, "amountCash", _amountCash);
return bribe(factionName, amountCash); return bribe(factionName, amountCash);
}, },
+9 -10
View File
@@ -1,16 +1,17 @@
import type { Gang as IGang, EquipmentStats, GangOtherInfoObject } from "@nsdefs";
import type { Gang } from "../Gang/Gang";
import type { GangMember } from "../Gang/GangMember";
import type { GangMemberTask } from "../Gang/GangMemberTask";
import type { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { Player } from "@player";
import { FactionName } from "@enums"; import { FactionName } from "@enums";
import { GangConstants } from "../Gang/data/Constants"; import { GangConstants } from "../Gang/data/Constants";
import { Player } from "@player";
import { Gang } from "../Gang/Gang";
import { AllGangs } from "../Gang/AllGangs"; import { AllGangs } from "../Gang/AllGangs";
import { GangMemberTasks } from "../Gang/GangMemberTasks"; import { GangMemberTasks } from "../Gang/GangMemberTasks";
import { GangMemberUpgrades } from "../Gang/GangMemberUpgrades"; import { GangMemberUpgrades } from "../Gang/GangMemberUpgrades";
import { GangMember } from "../Gang/GangMember";
import { GangMemberTask } from "../Gang/GangMemberTask";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { getEnumHelper } from "../utils/EnumHelper";
import { Gang as IGang, EquipmentStats, GangOtherInfoObject } from "@nsdefs";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
export function NetscriptGang(): InternalAPI<IGang> { export function NetscriptGang(): InternalAPI<IGang> {
/** Functions as an API check and also returns the gang object */ /** Functions as an API check and also returns the gang object */
@@ -36,9 +37,7 @@ export function NetscriptGang(): InternalAPI<IGang> {
return { return {
createGang: (ctx) => (_faction) => { createGang: (ctx) => (_faction) => {
const faction = helpers.string(ctx, "faction", _faction); const faction = getEnumHelper("FactionName").nsGetMember(ctx, _faction);
// this list is copied from Faction/ui/Root.tsx
if (!Player.canAccessGang() || !GangConstants.Names.includes(faction)) return false; if (!Player.canAccessGang() || !GangConstants.Names.includes(faction)) return false;
if (Player.gang) return false; if (Player.gang) return false;
if (!Player.factions.includes(faction)) return false; if (!Player.factions.includes(faction)) return false;
+19 -30
View File
@@ -1,6 +1,5 @@
import type { Singularity as ISingularity } from "@nsdefs"; import type { Singularity as ISingularity } from "@nsdefs";
import type { Company } from "../Company/Company"; import type { Company } from "../Company/Company";
import type { Faction } from "../Faction/Faction";
import { Player } from "@player"; import { Player } from "@player";
import { import {
@@ -33,7 +32,7 @@ import { formatMoney, formatRam, formatReputation } from "../ui/formatNumber";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Companies } from "../Company/Companies"; import { Companies } from "../Company/Companies";
import { companiesMetadata } from "../Company/data/CompaniesMetadata"; import { companiesMetadata } from "../Company/data/CompaniesMetadata";
import { Factions, factionExists } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { getServerOnNetwork } from "../Server/ServerHelpers"; import { getServerOnNetwork } from "../Server/ServerHelpers";
@@ -59,14 +58,6 @@ import { ScriptFilePath, resolveScriptFilePath } from "../Paths/ScriptFilePath";
import { root } from "../Paths/Directory"; import { root } from "../Paths/Directory";
export function NetscriptSingularity(): InternalAPI<ISingularity> { export function NetscriptSingularity(): InternalAPI<ISingularity> {
const getFaction = function (ctx: NetscriptContext, name: string): Faction {
if (!factionExists(name)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid faction name: '${name}`);
}
return Factions[name];
};
const getCompany = function (ctx: NetscriptContext, name: string): Company { const getCompany = function (ctx: NetscriptContext, name: string): Company {
const company = Companies[name]; const company = Companies[name];
if (!company) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company name: '${name}'`); if (!company) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company name: '${name}'`);
@@ -112,9 +103,8 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}, },
getAugmentationsFromFaction: (ctx) => (_facName) => { getAugmentationsFromFaction: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName); const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const faction = getFaction(ctx, facName); const faction = Factions[facName];
return getFactionAugmentationsFiltered(faction); return getFactionAugmentationsFiltered(faction);
}, },
getAugmentationPrereq: (ctx) => (_augName) => { getAugmentationPrereq: (ctx) => (_augName) => {
@@ -149,19 +139,19 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}, },
purchaseAugmentation: (ctx) => (_facName, _augName) => { purchaseAugmentation: (ctx) => (_facName, _augName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName); const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName); const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
const fac = getFaction(ctx, facName); const fac = Factions[facName];
const aug = Augmentations[augName]; const aug = Augmentations[augName];
const augs = getFactionAugmentationsFiltered(fac); const factionAugs = getFactionAugmentationsFiltered(fac);
if (!Player.factions.includes(fac.name)) { if (!Player.factions.includes(facName)) {
helpers.log(ctx, () => `You can't purchase augmentations from '${facName}' because you aren't a member`); helpers.log(ctx, () => `You can't purchase augmentations from '${facName}' because you aren't a member`);
return false; return false;
} }
if (!augs.includes(augName)) { if (!factionAugs.includes(augName)) {
helpers.log(ctx, () => `Faction '${facName}' does not have the '${augName}' augmentation.`); helpers.log(ctx, () => `Faction '${facName}' does not have the '${augName}' augmentation.`);
return false; return false;
} }
@@ -867,8 +857,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}, },
joinFaction: (ctx) => (_facName) => { joinFaction: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName); const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
getFaction(ctx, facName);
if (!Player.factionInvitations.includes(facName)) { if (!Player.factionInvitations.includes(facName)) {
helpers.log(ctx, () => `You have not been invited by faction '${facName}'`); helpers.log(ctx, () => `You have not been invited by faction '${facName}'`);
@@ -892,10 +881,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
(ctx) => (ctx) =>
(_facName, _type, _focus = true) => { (_facName, _type, _focus = true) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName); const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const type = helpers.string(ctx, "type", _type); const type = helpers.string(ctx, "type", _type);
const focus = !!_focus; const focus = !!_focus;
const faction = getFaction(ctx, facName); const faction = Factions[facName];
// if the player is in a gang and the target faction is any of the gang faction, fail // if the player is in a gang and the target faction is any of the gang faction, fail
if (Player.gang && faction.name === Player.getGangFaction().name) { if (Player.gang && faction.name === Player.getGangFaction().name) {
@@ -987,27 +976,27 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}, },
getFactionRep: (ctx) => (_facName) => { getFactionRep: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName); const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const faction = getFaction(ctx, facName); const faction = Factions[facName];
return faction.playerReputation; return faction.playerReputation;
}, },
getFactionFavor: (ctx) => (_facName) => { getFactionFavor: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName); const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const faction = getFaction(ctx, facName); const faction = Factions[facName];
return faction.favor; return faction.favor;
}, },
getFactionFavorGain: (ctx) => (_facName) => { getFactionFavorGain: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName); const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const faction = getFaction(ctx, facName); const faction = Factions[facName];
return faction.getFavorGain(); return faction.getFavorGain();
}, },
donateToFaction: (ctx) => (_facName, _amt) => { donateToFaction: (ctx) => (_facName, _amt) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName); const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const amt = helpers.number(ctx, "amt", _amt); const amt = helpers.number(ctx, "amt", _amt);
const faction = getFaction(ctx, facName); const faction = Factions[facName];
if (!Player.factions.includes(faction.name)) { if (!Player.factions.includes(faction.name)) {
helpers.log(ctx, () => `You can't donate to '${facName}' because you aren't a member`); helpers.log(ctx, () => `You can't donate to '${facName}' because you aren't a member`);
return false; return false;
+3 -3
View File
@@ -17,7 +17,7 @@ import * as serverMethods from "./PlayerObjectServerMethods";
import * as workMethods from "./PlayerObjectWorkMethods"; import * as workMethods from "./PlayerObjectWorkMethods";
import { setPlayer } from "../../Player"; import { setPlayer } from "../../Player";
import { LocationName } from "@enums"; import { FactionName, LocationName } from "@enums";
import { HashManager } from "../../Hacknet/HashManager"; import { HashManager } from "../../Hacknet/HashManager";
import { MoneySourceTracker } from "../../utils/MoneySourceTracker"; import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../../utils/JSONReviver"; import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../../utils/JSONReviver";
@@ -35,8 +35,8 @@ export class PlayerObject extends Person implements IPlayer {
gang: Gang | null = null; gang: Gang | null = null;
bladeburner: Bladeburner | null = null; bladeburner: Bladeburner | null = null;
currentServer = ""; currentServer = "";
factions: string[] = []; factions: FactionName[] = [];
factionInvitations: string[] = []; factionInvitations: FactionName[] = [];
hacknetNodes: (HacknetNode | string)[] = []; // HacknetNode object or hostname of Hacknet Server hacknetNodes: (HacknetNode | string)[] = []; // HacknetNode object or hostname of Hacknet Server
has4SData = false; has4SData = false;
has4SDataTixApi = false; has4SDataTixApi = false;
@@ -1,11 +1,12 @@
import type { PlayerObject } from "./PlayerObject";
import type { FactionName } from "@enums";
import type { Faction } from "../../Faction/Faction";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { Faction } from "../../Faction/Faction";
import { Gang } from "../../Gang/Gang"; import { Gang } from "../../Gang/Gang";
import { GangConstants } from "../../Gang/data/Constants"; import { GangConstants } from "../../Gang/data/Constants";
import { isFactionWork } from "../../Work/FactionWork"; import { isFactionWork } from "../../Work/FactionWork";
import type { PlayerObject } from "./PlayerObject";
export function canAccessGang(this: PlayerObject): boolean { export function canAccessGang(this: PlayerObject): boolean {
if (this.bitNodeN === 2) { if (this.bitNodeN === 2) {
return true; return true;
@@ -36,12 +37,12 @@ export function getGangName(this: PlayerObject): string {
return gang ? gang.facName : ""; return gang ? gang.facName : "";
} }
export function hasGangWith(this: PlayerObject, facName: string): boolean { export function hasGangWith(this: PlayerObject, facName: FactionName): boolean {
const gang = this.gang; const gang = this.gang;
return gang ? gang.facName === facName : false; return gang ? gang.facName === facName : false;
} }
export function startGang(this: PlayerObject, factionName: string, hacking: boolean): void { export function startGang(this: PlayerObject, factionName: FactionName, hacking: boolean): void {
// isFactionWork handles null internally, finishWork might need to be run with true // isFactionWork handles null internally, finishWork might need to be run with true
if (isFactionWork(this.currentWork) && this.currentWork.factionName === factionName) this.finishWork(false); if (isFactionWork(this.currentWork) && this.currentWork.factionName === factionName) this.finishWork(false);
@@ -158,7 +158,7 @@ export function prestigeSourceFile(this: PlayerObject): void {
this.augmentations = []; this.augmentations = [];
} }
export function receiveInvite(this: PlayerObject, factionName: string): void { export function receiveInvite(this: PlayerObject, factionName: FactionName): void {
if (this.factionInvitations.includes(factionName) || this.factions.includes(factionName)) { if (this.factionInvitations.includes(factionName) || this.factions.includes(factionName)) {
return; return;
} }
@@ -1099,34 +1099,34 @@ export function gainCodingContractReward(
reward: ICodingContractReward | null, reward: ICodingContractReward | null,
difficulty = 1, difficulty = 1,
): string { ): string {
if (reward == null || reward.type == null) { if (!reward) return `No reward for this contract`;
return `No reward for this contract`;
}
/* eslint-disable no-case-declarations */ /* eslint-disable no-case-declarations */
switch (reward.type) { switch (reward.type) {
case CodingContractRewardType.FactionReputation: case CodingContractRewardType.FactionReputation: {
if (reward.name == null || !Factions[reward.name]) { if (!Factions[reward.name]) {
// If no/invalid faction was designated, just give rewards to all factions return this.gainCodingContractReward({ type: CodingContractRewardType.FactionReputationAll });
reward.type = CodingContractRewardType.FactionReputationAll;
return this.gainCodingContractReward(reward);
} }
const repGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty; const repGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty;
Factions[reward.name].playerReputation += repGain; Factions[reward.name].playerReputation += repGain;
return `Gained ${repGain} faction reputation for ${reward.name}`; return `Gained ${repGain} faction reputation for ${reward.name}`;
case CodingContractRewardType.FactionReputationAll: }
case CodingContractRewardType.FactionReputationAll: {
const totalGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty; const totalGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty;
// Ignore Bladeburners and other special factions for this calculation // Ignore Bladeburners and other special factions for this calculation
const specialFactions = [FactionName.Bladeburners as string]; const specialFactions = [
FactionName.Bladeburners,
FactionName.ShadowsOfAnarchy,
FactionName.ChurchOfTheMachineGod,
];
const factions = this.factions.slice().filter((f) => { const factions = this.factions.slice().filter((f) => {
return !specialFactions.includes(f); return !specialFactions.includes(f);
}); });
// If the player was only part of the special factions, we'll just give money // If the player was only part of the special factions, we'll just give money
if (factions.length == 0) { if (factions.length == 0) {
reward.type = CodingContractRewardType.Money; return this.gainCodingContractReward({ type: CodingContractRewardType.Money }, difficulty);
return this.gainCodingContractReward(reward, difficulty);
} }
const gainPerFaction = Math.floor(totalGain / factions.length); const gainPerFaction = Math.floor(totalGain / factions.length);
@@ -1135,11 +1135,11 @@ export function gainCodingContractReward(
Factions[facName].playerReputation += gainPerFaction; Factions[facName].playerReputation += gainPerFaction;
} }
return `Gained ${gainPerFaction} reputation for each of the following factions: ${factions.join(", ")}`; return `Gained ${gainPerFaction} reputation for each of the following factions: ${factions.join(", ")}`;
}
case CodingContractRewardType.CompanyReputation: { case CodingContractRewardType.CompanyReputation: {
if (reward.name == null || !Companies[reward.name]) { if (!Companies[reward.name]) {
//If no/invalid company was designated, just give rewards to all factions //If no/invalid company was designated, just give rewards to all factions
reward.type = CodingContractRewardType.FactionReputationAll; return this.gainCodingContractReward({ type: CodingContractRewardType.FactionReputationAll });
return this.gainCodingContractReward(reward);
} }
const repGain = CONSTANTS.CodingContractBaseCompanyRepGain * difficulty; const repGain = CONSTANTS.CodingContractBaseCompanyRepGain * difficulty;
Companies[reward.name].playerReputation += repGain; Companies[reward.name].playerReputation += repGain;
+16 -23
View File
@@ -292,38 +292,31 @@ export class Sleeve extends Person implements SleevePerson {
return true; return true;
} }
/** /** TODO 2.4: Make this take in type correct data */
* Start work for one of the player's factions workForFaction(_factionName: string, _workType: string): boolean {
* Returns boolean indicating success const factionName = getEnumHelper("FactionName").fuzzyGetMember(_factionName);
*/ if (!factionName) return false;
workForFaction(factionName: string, workType: string): boolean {
const faction = Factions[factionName]; const faction = Factions[factionName];
if (factionName === "" || !faction || !Player.factions.includes(factionName)) { const workType = getEnumHelper("FactionWorkType").fuzzyGetMember(_workType);
return false; if (!workType) return false;
}
const factionInfo = faction.getInfo(); const factionInfo = faction.getInfo();
// Set type of work (hacking/field/security), and the experience gains switch (workType) {
const sanitizedWorkType = workType.toLowerCase(); case FactionWorkType.field:
let factionWorkType: FactionWorkType;
if (sanitizedWorkType.includes("hack")) {
if (!factionInfo.offerHackingWork) return false;
factionWorkType = FactionWorkType.hacking;
} else if (sanitizedWorkType.includes("field")) {
if (!factionInfo.offerFieldWork) return false; if (!factionInfo.offerFieldWork) return false;
factionWorkType = FactionWorkType.field; break;
} else if (sanitizedWorkType.includes("security")) { case FactionWorkType.hacking:
if (!factionInfo.offerHackingWork) return false;
break;
case FactionWorkType.security:
if (!factionInfo.offerSecurityWork) return false; if (!factionInfo.offerSecurityWork) return false;
factionWorkType = FactionWorkType.security; break;
} else {
return false;
} }
this.startWork( this.startWork(
new SleeveFactionWork({ new SleeveFactionWork({
factionWorkType: factionWorkType, factionWorkType: workType,
factionName: faction.name, factionName: factionName,
}), }),
); );
@@ -7,11 +7,11 @@ import { Factions } from "../../../Faction/Factions";
import { calculateFactionExp, calculateFactionRep } from "../../../Work/Formulas"; import { calculateFactionExp, calculateFactionRep } from "../../../Work/Formulas";
import { Faction } from "../../../Faction/Faction"; import { Faction } from "../../../Faction/Faction";
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats"; import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { findEnumMember } from "../../../utils/helpers/enum"; import { getEnumHelper } from "../../../utils/EnumHelper";
interface SleeveFactionWorkParams { interface SleeveFactionWorkParams {
factionWorkType: FactionWorkType; factionWorkType: FactionWorkType;
factionName: string; factionName: FactionName;
} }
export const isSleeveFactionWork = (w: SleeveWorkClass | null): w is SleeveFactionWork => export const isSleeveFactionWork = (w: SleeveWorkClass | null): w is SleeveFactionWork =>
@@ -20,7 +20,7 @@ export const isSleeveFactionWork = (w: SleeveWorkClass | null): w is SleeveFacti
export class SleeveFactionWork extends SleeveWorkClass { export class SleeveFactionWork extends SleeveWorkClass {
type: SleeveWorkType.FACTION = SleeveWorkType.FACTION; type: SleeveWorkType.FACTION = SleeveWorkType.FACTION;
factionWorkType: FactionWorkType; factionWorkType: FactionWorkType;
factionName: string; factionName: FactionName;
constructor(params?: SleeveFactionWorkParams) { constructor(params?: SleeveFactionWorkParams) {
super(); super();
@@ -67,8 +67,8 @@ export class SleeveFactionWork extends SleeveWorkClass {
/** Initializes a FactionWork object from a JSON save state. */ /** Initializes a FactionWork object from a JSON save state. */
static fromJSON(value: IReviverValue): SleeveFactionWork { static fromJSON(value: IReviverValue): SleeveFactionWork {
const factionWork = Generic_fromJSON(SleeveFactionWork, value.data); const factionWork = Generic_fromJSON(SleeveFactionWork, value.data);
factionWork.factionWorkType = factionWork.factionWorkType = getEnumHelper("FactionWorkType").fuzzyGetMember(factionWork.factionWorkType, true);
findEnumMember(FactionWorkType, factionWork.factionWorkType) ?? FactionWorkType.hacking; factionWork.factionName = getEnumHelper("FactionName").fuzzyGetMember(factionWork.factionName, true);
return factionWork; return factionWork;
} }
} }
+2 -3
View File
@@ -137,10 +137,9 @@ const tasks: {
return { return {
first: factions, first: factions,
second: (s1: string) => { second: (s1) => {
if (!getEnumHelper("FactionName").isMember(s1)) return ["------"];
const faction = Factions[s1]; const faction = Factions[s1];
if (!faction) return ["------"];
const facInfo = faction.getInfo(); const facInfo = faction.getInfo();
const options: string[] = []; const options: string[] = [];
if (facInfo.offerHackingWork) { if (facInfo.offerHackingWork) {
+9 -7
View File
@@ -2,7 +2,7 @@ import { AugmentationName, CityName, CompletedProgramName, FactionName, Literatu
import { initBitNodeMultipliers } from "./BitNode/BitNode"; import { initBitNodeMultipliers } from "./BitNode/BitNode";
import { Companies, initCompanies } from "./Company/Companies"; import { Companies, initCompanies } from "./Company/Companies";
import { resetIndustryResearchTrees } from "./Corporation/data/IndustryData"; import { resetIndustryResearchTrees } from "./Corporation/data/IndustryData";
import { Factions, initFactions } from "./Faction/Factions"; import { Factions } from "./Faction/Factions";
import { joinFaction } from "./Faction/FactionHelpers"; import { joinFaction } from "./Faction/FactionHelpers";
import { updateHashManagerCapacity } from "./Hacknet/HacknetHelpers"; import { updateHashManagerCapacity } from "./Hacknet/HacknetHelpers";
import { prestigeWorkerScripts } from "./NetscriptWorker"; import { prestigeWorkerScripts } from "./NetscriptWorker";
@@ -23,6 +23,7 @@ import { ProgramsSeen } from "./Programs/ui/ProgramsRoot";
import { InvitationsSeen } from "./Faction/ui/FactionsRoot"; import { InvitationsSeen } from "./Faction/ui/FactionsRoot";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { LogBoxClearEvents } from "./ui/React/LogBoxManager"; import { LogBoxClearEvents } from "./ui/React/LogBoxManager";
import { initCircadianModulator } from "./Augmentation/Augmentations";
const BitNode8StartingMoney = 250e6; const BitNode8StartingMoney = 250e6;
function delayedDialog(message: string) { function delayedDialog(message: string) {
@@ -71,7 +72,7 @@ export function prestigeAugmentation(): void {
// Gain favor for Companies and Factions // Gain favor for Companies and Factions
for (const company of Object.values(Companies)) company.gainFavor(); for (const company of Object.values(Companies)) company.gainFavor();
for (const faction of Object.values(Factions)) faction.gainFavor(); for (const faction of Object.values(Factions)) faction.prestigeAugmentation();
// Stop a Terminal action if there is one. // Stop a Terminal action if there is one.
if (Terminal.action !== null) { if (Terminal.action !== null) {
@@ -80,10 +81,11 @@ export function prestigeAugmentation(): void {
Terminal.clear(); Terminal.clear();
LogBoxClearEvents.emit(); LogBoxClearEvents.emit();
// Re-initialize things - This will update any changes // Recalculate the bonus for circadian modulator aug
initFactions(); // Factions must be initialized before augmentations initCircadianModulator();
Player.factionInvitations = Player.factionInvitations.concat(maintainMembership); Player.factionInvitations = Player.factionInvitations.concat(maintainMembership);
for (const factionName of maintainMembership) Factions[factionName].alreadyInvited = true;
Player.reapplyAllAugmentations(); Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles(); Player.reapplyAllSourceFiles();
Player.hp.current = Player.hp.max; Player.hp.current = Player.hp.max;
@@ -193,7 +195,7 @@ export function prestigeSourceFile(isFlume: boolean): void {
// Reset favor for Companies and Factions // Reset favor for Companies and Factions
for (const company of Object.values(Companies)) company.favor = 0; for (const company of Object.values(Companies)) company.favor = 0;
for (const faction of Object.values(Factions)) faction.favor = 0; for (const faction of Object.values(Factions)) faction.prestigeSourceFile();
// Stop a Terminal action if there is one // Stop a Terminal action if there is one
if (Terminal.action !== null) { if (Terminal.action !== null) {
@@ -208,8 +210,8 @@ export function prestigeSourceFile(isFlume: boolean): void {
}); });
} }
// Re-initialize things - This will update any changes initCircadianModulator();
initFactions(); // Factions must be initialized before augmentations
Player.reapplyAllAugmentations(); Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles(); Player.reapplyAllSourceFiles();
initCompanies(); initCompanies();
+8 -9
View File
@@ -1,28 +1,29 @@
import type { Faction } from "../Faction/Faction";
import React from "react"; import React from "react";
import { Work, WorkType } from "./Work"; import { Work, WorkType } from "./Work";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver"; import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { Player } from "@player"; import { Player } from "@player";
import { AugmentationName, FactionName, FactionWorkType } from "@enums"; import { AugmentationName, FactionName, FactionWorkType } from "@enums";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { Faction } from "../Faction/Faction";
import { applyWorkStats, scaleWorkStats, WorkStats } from "./WorkStats"; import { applyWorkStats, scaleWorkStats, WorkStats } from "./WorkStats";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reputation } from "../ui/React/Reputation"; import { Reputation } from "../ui/React/Reputation";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { calculateFactionExp, calculateFactionRep } from "./Formulas"; import { calculateFactionExp, calculateFactionRep } from "./Formulas";
import { findEnumMember } from "../utils/helpers/enum"; import { getEnumHelper } from "../utils/EnumHelper";
interface FactionWorkParams { interface FactionWorkParams {
singularity: boolean; singularity: boolean;
factionWorkType: FactionWorkType; factionWorkType: FactionWorkType;
faction: string; faction: FactionName;
} }
export const isFactionWork = (w: Work | null): w is FactionWork => w !== null && w.type === WorkType.FACTION; export const isFactionWork = (w: Work | null): w is FactionWork => w !== null && w.type === WorkType.FACTION;
export class FactionWork extends Work { export class FactionWork extends Work {
factionWorkType: FactionWorkType; factionWorkType: FactionWorkType;
factionName: string; factionName: FactionName;
constructor(params?: FactionWorkParams) { constructor(params?: FactionWorkParams) {
super(WorkType.FACTION, params?.singularity ?? true); super(WorkType.FACTION, params?.singularity ?? true);
@@ -31,9 +32,7 @@ export class FactionWork extends Work {
} }
getFaction(): Faction { getFaction(): Faction {
const f = Factions[this.factionName]; return Factions[this.factionName];
if (!f) throw new Error(`Faction work started with invalid / unknown faction: '${this.factionName}'`);
return f;
} }
getReputationRate(): number { getReputationRate(): number {
@@ -92,8 +91,8 @@ export class FactionWork extends Work {
/** Initializes a FactionWork object from a JSON save state. */ /** Initializes a FactionWork object from a JSON save state. */
static fromJSON(value: IReviverValue): FactionWork { static fromJSON(value: IReviverValue): FactionWork {
const factionWork = Generic_fromJSON(FactionWork, value.data); const factionWork = Generic_fromJSON(FactionWork, value.data);
factionWork.factionWorkType = factionWork.factionWorkType = getEnumHelper("FactionWorkType").fuzzyGetMember(factionWork.factionWorkType, true);
findEnumMember(FactionWorkType, factionWork.factionWorkType) ?? FactionWorkType.hacking; factionWork.factionName = getEnumHelper("FactionName").fuzzyGetMember(factionWork.factionName, true);
return factionWork; return factionWork;
} }
} }
+1 -2
View File
@@ -5,7 +5,7 @@ import { initSourceFiles } from "./SourceFile/SourceFiles";
import { generateRandomContract } from "./CodingContractGenerator"; import { generateRandomContract } from "./CodingContractGenerator";
import { initCompanies } from "./Company/Companies"; import { initCompanies } from "./Company/Companies";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { Factions, initFactions } from "./Faction/Factions"; import { Factions } from "./Faction/Factions";
import { staneksGift } from "./CotMG/Helper"; import { staneksGift } from "./CotMG/Helper";
import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers"; import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers";
import { Router } from "./ui/GameRoot"; import { Router } from "./ui/GameRoot";
@@ -375,7 +375,6 @@ const Engine: {
Player.init(); Player.init();
initForeignServers(Player.getHomeComputer()); initForeignServers(Player.getHomeComputer());
initCompanies(); initCompanies();
initFactions();
Player.reapplyAllAugmentations(); Player.reapplyAllAugmentations();
// Start interactive tutorial // Start interactive tutorial
+6
View File
@@ -1,4 +1,6 @@
/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */ /* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */
import type { Unknownify } from "../types";
import { ObjectValidator, validateObject } from "./Validator"; import { ObjectValidator, validateObject } from "./Validator";
import { JSONMap, JSONSet } from "../Types/Jsonable"; import { JSONMap, JSONSet } from "../Types/Jsonable";
@@ -102,3 +104,7 @@ export function Generic_fromJSON<T extends Record<string, any>>(
for (const [key, val] of Object.entries(data) as [keyof T, T[keyof T]][]) obj[key] = val; for (const [key, val] of Object.entries(data) as [keyof T, T[keyof T]][]) obj[key] = val;
return obj; return obj;
} }
// This function is empty because Unknownify<T> is a typesafe assertion on any object with no runtime checks needed.
// eslint-disable-next-line @typescript-eslint/no-empty-function
export function assertLoadingType<T extends object>(val: object): asserts val is Unknownify<T> {}
+1 -2
View File
@@ -1,5 +1,5 @@
import { saveObject } from "../../src/SaveObject"; import { saveObject } from "../../src/SaveObject";
import { Factions, initFactions } from "../../src/Faction/Factions"; import { Factions } from "../../src/Faction/Factions";
import { Player, setPlayer } from "../../src/Player"; import { Player, setPlayer } from "../../src/Player";
import { PlayerObject } from "../../src/PersonObjects/Player/PlayerObject"; import { PlayerObject } from "../../src/PersonObjects/Player/PlayerObject";
import { joinFaction } from "../../src/Faction/FactionHelpers"; import { joinFaction } from "../../src/Faction/FactionHelpers";
@@ -33,7 +33,6 @@ function establishInitialConditions() {
setPlayer(new PlayerObject()); setPlayer(new PlayerObject());
Player.init(); Player.init();
Player.identifier = "Overwritten identifier"; Player.identifier = "Overwritten identifier";
initFactions();
Player.sleevesFromCovenant = 1; Player.sleevesFromCovenant = 1;
Player.sourceFiles.set(10, 1); Player.sourceFiles.set(10, 1);
Player.prestigeAugmentation(); Player.prestigeAugmentation();
@@ -6,19 +6,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Wired Reflexes",
"Speech Processor Implant",
"Synaptic Enhancement Implant",
"Neuralstimulator",
"PCMatrix",
"NeuroFlux Governor",
"Neurotrainer I",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Aevum",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -26,20 +16,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Enhanced Social Interaction Implant",
"Neuralstimulator",
"Nuoptimal Nootropic Injector Implant",
"Speech Enhancement",
"FocusWire",
"ADR-V2 Pheromone Gene",
"NeuroFlux Governor",
"SmartJaw",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Bachman & Associates",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -47,25 +26,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Artificial Bio-neural Network Implant",
"Enhanced Myelin Sheathing",
"DataJack",
"Embedded Netburner Module",
"Embedded Netburner Module Core Implant",
"Embedded Netburner Module Core V2 Upgrade",
"Neural Accelerator",
"Cranial Signal Processors - Gen III",
"Cranial Signal Processors - Gen IV",
"Cranial Signal Processors - Gen V",
"NeuroFlux Governor",
"Neurotrainer II",
"BitRunners Neurolink",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "BitRunners",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -73,30 +36,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Augmented Targeting I",
"Augmented Targeting II",
"Augmented Targeting III",
"Synfibril Muscle",
"Combat Rib I",
"Combat Rib II",
"Combat Rib III",
"Nanofiber Weave",
"Bionic Spine",
"Bionic Legs",
"Embedded Netburner Module",
"Embedded Netburner Module Core Implant",
"Embedded Netburner Module Core V2 Upgrade",
"PC Direct-Neural Interface",
"PC Direct-Neural Interface Optimization Submodule",
"NeuroFlux Governor",
"HyperSight Corneal Implant",
"Neotra",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Blade Industries",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -104,29 +46,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"EsperTech Bladeburner Eyewear",
"EMS-4 Recombination",
"ORION-MKIV Shoulder",
"Hyperion Plasma Cannon V1",
"Hyperion Plasma Cannon V2",
"GOLEM Serum",
"Vangelis Virus",
"Vangelis Virus 3.0",
"I.N.T.E.R.L.I.N.K.E.D",
"Blade's Runners",
"BLADE-51b Tesla Armor",
"BLADE-51b Tesla Armor: Power Cells Upgrade",
"BLADE-51b Tesla Armor: Energy Shielding Upgrade",
"BLADE-51b Tesla Armor: Unibeam Upgrade",
"BLADE-51b Tesla Armor: Omnibeam Upgrade",
"BLADE-51b Tesla Armor: IPU Upgrade",
"The Blade's Simulacrum",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": true, "isMember": true,
"name": "Bladeburners",
"playerReputation": 4000, "playerReputation": 4000,
}, },
}, },
@@ -134,18 +56,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Speech Processor Implant",
"DataJack",
"Neuralstimulator",
"Nuoptimal Nootropic Injector Implant",
"NeuroFlux Governor",
"Neuregen Gene Modification",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Chongqing",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -153,15 +66,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Stanek's Gift - Genesis",
"Stanek's Gift - Awakening",
"Stanek's Gift - Serenity",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Church of the Machine God",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -169,21 +76,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Enhanced Social Interaction Implant",
"Neuralstimulator",
"Neuronal Densification",
"Nuoptimal Nootropic Injector Implant",
"Speech Enhancement",
"FocusWire",
"ADR-V2 Pheromone Gene",
"NeuroFlux Governor",
"nextSENS Gene Modification",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Clarke Incorporated",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -191,18 +86,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"BitWire",
"Synaptic Enhancement Implant",
"Cranial Signal Processors - Gen I",
"Cranial Signal Processors - Gen II",
"NeuroFlux Governor",
"Neurotrainer I",
],
"favor": 20, "favor": 20,
"isBanned": false, "isBanned": false,
"isMember": true, "isMember": true,
"name": "CyberSec",
"playerReputation": 1000000, "playerReputation": 1000000,
}, },
}, },
@@ -210,20 +96,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Synthetic Heart",
"Synfibril Muscle",
"NEMEAN Subdermal Weave",
"Embedded Netburner Module Core V3 Upgrade",
"Embedded Netburner Module Analyze Engine",
"Embedded Netburner Module Direct Memory Access Upgrade",
"NeuroFlux Governor",
"The Red Pill",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Daedalus",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -231,24 +106,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Graphene Bionic Spine Upgrade",
"Graphene Bionic Legs Upgrade",
"Embedded Netburner Module",
"Embedded Netburner Module Core Implant",
"Embedded Netburner Module Core V2 Upgrade",
"Embedded Netburner Module Core V3 Upgrade",
"Embedded Netburner Module Analyze Engine",
"Embedded Netburner Module Direct Memory Access Upgrade",
"PC Direct-Neural Interface",
"PC Direct-Neural Interface Optimization Submodule",
"NeuroFlux Governor",
"ECorp HVMind Implant",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "ECorp",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -256,22 +116,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Enhanced Social Interaction Implant",
"Neuralstimulator",
"Nuoptimal Nootropic Injector Implant",
"Speech Enhancement",
"FocusWire",
"PC Direct-Neural Interface",
"ADR-V1 Pheromone Gene",
"ADR-V2 Pheromone Gene",
"NeuroFlux Governor",
"Neurotrainer III",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Four Sigma",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -279,30 +126,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Synthetic Heart",
"Synfibril Muscle",
"Nanofiber Weave",
"NEMEAN Subdermal Weave",
"Graphene Bone Lacings",
"Graphene Bionic Spine Upgrade",
"Graphene Bionic Legs Upgrade",
"Artificial Bio-neural Network Implant",
"Enhanced Myelin Sheathing",
"Embedded Netburner Module",
"Embedded Netburner Module Core Implant",
"Embedded Netburner Module Core V2 Upgrade",
"Embedded Netburner Module Core V3 Upgrade",
"Embedded Netburner Module Analyze Engine",
"Embedded Netburner Module Direct Memory Access Upgrade",
"PC Direct-Neural Interface Optimization Submodule",
"PC Direct-Neural Interface NeuroNet Injector",
"NeuroFlux Governor",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Fulcrum Secret Technologies",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -310,20 +136,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Synthetic Heart",
"Synfibril Muscle",
"NEMEAN Subdermal Weave",
"Embedded Netburner Module Core V3 Upgrade",
"Embedded Netburner Module Analyze Engine",
"Embedded Netburner Module Direct Memory Access Upgrade",
"NeuroFlux Governor",
"QLink",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Illuminati",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -331,19 +146,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Augmented Targeting I",
"Combat Rib I",
"Wired Reflexes",
"Speech Processor Implant",
"Neuralstimulator",
"NeuroFlux Governor",
"INFRARET Enhancement",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Ishima",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -351,28 +156,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Augmented Targeting I",
"Augmented Targeting II",
"Augmented Targeting III",
"Synthetic Heart",
"Synfibril Muscle",
"Combat Rib I",
"Combat Rib II",
"Combat Rib III",
"Bionic Spine",
"Bionic Legs",
"Embedded Netburner Module Core V2 Upgrade",
"Speech Enhancement",
"FocusWire",
"NeuroFlux Governor",
"HyperSight Corneal Implant",
"Photosynthetic Cells",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "KuaiGong International",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -380,22 +166,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Graphene Bionic Legs Upgrade",
"Embedded Netburner Module",
"Embedded Netburner Module Core Implant",
"Embedded Netburner Module Core V2 Upgrade",
"Embedded Netburner Module Core V3 Upgrade",
"Embedded Netburner Module Analyze Engine",
"Embedded Netburner Module Direct Memory Access Upgrade",
"ADR-V1 Pheromone Gene",
"NeuroFlux Governor",
"CordiARC Fusion Reactor",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "MegaCorp",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -403,27 +176,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Synthetic Heart",
"Synfibril Muscle",
"Enhanced Social Interaction Implant",
"Embedded Netburner Module",
"Embedded Netburner Module Core Implant",
"Embedded Netburner Module Core V2 Upgrade",
"Embedded Netburner Module Core V3 Upgrade",
"Embedded Netburner Module Analyze Engine",
"Embedded Netburner Module Direct Memory Access Upgrade",
"ADR-V1 Pheromone Gene",
"NeuroFlux Governor",
"Neurotrainer III",
"Power Recirculation Core",
"Xanipher",
"Hydroflame Left Arm",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "NWO",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -431,18 +186,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Hacknet Node CPU Architecture Neural-Upload",
"Hacknet Node Cache Architecture Neural-Upload",
"Hacknet Node NIC Architecture Neural-Upload",
"Hacknet Node Kernel Direct-Neural Interface",
"Hacknet Node Core Direct-Neural Interface",
"NeuroFlux Governor",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Netburners",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -450,18 +196,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Speech Processor Implant",
"DataJack",
"Neuralstimulator",
"Nuoptimal Nootropic Injector Implant",
"NeuroFlux Governor",
"NutriGen Implant",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "New Tokyo",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -469,23 +206,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"BitWire",
"Artificial Synaptic Potentiation",
"Neural-Retention Enhancement",
"DataJack",
"Embedded Netburner Module",
"Cranial Signal Processors - Gen I",
"Cranial Signal Processors - Gen II",
"Cranial Signal Processors - Gen III",
"NeuroFlux Governor",
"Neurotrainer II",
"CRTX42-AA Gene Modification",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "NiteSec",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -493,26 +216,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Augmented Targeting I",
"Augmented Targeting II",
"Augmented Targeting III",
"Combat Rib I",
"Combat Rib II",
"Combat Rib III",
"Nanofiber Weave",
"Bionic Spine",
"Bionic Legs",
"Enhanced Social Interaction Implant",
"Embedded Netburner Module Core V2 Upgrade",
"PC Direct-Neural Interface",
"NeuroFlux Governor",
"OmniTek InfoLoad",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "OmniTek Incorporated",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -520,19 +226,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Augmented Targeting I",
"Augmented Targeting II",
"Wired Reflexes",
"Speech Processor Implant",
"Neuralstimulator",
"NeuroFlux Governor",
"CashRoot Starter Kit",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Sector-12",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -540,21 +236,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"SoA - Might of Ares",
"SoA - Wisdom of Athena",
"SoA - Trickery of Hermes",
"SoA - Beauty of Aphrodite",
"SoA - Chaos of Dionysus",
"SoA - Flood of Poseidon",
"SoA - Hunt of Artemis",
"SoA - Knowledge of Apollo",
"SoA - phyzical WKS harmonizer",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Shadows of Anarchy",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -562,16 +246,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Speech Processor Implant",
"TITN-41 Gene-Modification Injection",
"ADR-V2 Pheromone Gene",
"NeuroFlux Governor",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Silhouette",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -579,19 +256,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Augmented Targeting I",
"Combat Rib I",
"Wired Reflexes",
"NeuroFlux Governor",
"LuminCloaking-V1 Skin Implant",
"LuminCloaking-V2 Skin Implant",
"SmartSonar Implant",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": true, "isMember": true,
"name": "Slum Snakes",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -599,23 +266,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Synthetic Heart",
"Synfibril Muscle",
"Nanofiber Weave",
"Wired Reflexes",
"Bionic Spine",
"Bionic Legs",
"Speech Enhancement",
"The Shadow's Simulacrum",
"NeuroFlux Governor",
"Unstable Circadian Modulator",
"Graphene BrachiBlades Upgrade",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Speakers for the Dead",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -623,18 +276,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"NeuroFlux Governor",
"LuminCloaking-V1 Skin Implant",
"LuminCloaking-V2 Skin Implant",
"HemoRecirculator",
"Power Recirculation Core",
"Bionic Arms",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Tetrads",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -642,22 +286,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Artificial Synaptic Potentiation",
"Enhanced Myelin Sheathing",
"DataJack",
"Embedded Netburner Module",
"Embedded Netburner Module Core Implant",
"Neuralstimulator",
"Cranial Signal Processors - Gen III",
"Cranial Signal Processors - Gen IV",
"NeuroFlux Governor",
"The Black Hand",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "The Black Hand",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -665,23 +296,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Augmented Targeting III",
"Synthetic Heart",
"Synfibril Muscle",
"Combat Rib III",
"NEMEAN Subdermal Weave",
"Graphene Bone Lacings",
"Embedded Netburner Module Core V3 Upgrade",
"Embedded Netburner Module Analyze Engine",
"Embedded Netburner Module Direct Memory Access Upgrade",
"NeuroFlux Governor",
"SPTN-97 Gene Modification",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "The Covenant",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -689,25 +306,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Augmented Targeting I",
"Augmented Targeting II",
"Augmented Targeting III",
"Combat Rib I",
"Combat Rib II",
"Combat Rib III",
"Nanofiber Weave",
"Wired Reflexes",
"The Shadow's Simulacrum",
"NeuroFlux Governor",
"HemoRecirculator",
"Power Recirculation Core",
"Graphene Bionic Arms Upgrade",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "The Dark Army",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -715,29 +316,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Augmented Targeting I",
"Augmented Targeting II",
"Augmented Targeting III",
"Combat Rib I",
"Combat Rib II",
"Combat Rib III",
"Nanofiber Weave",
"NEMEAN Subdermal Weave",
"Wired Reflexes",
"Bionic Spine",
"Bionic Legs",
"ADR-V1 Pheromone Gene",
"The Shadow's Simulacrum",
"NeuroFlux Governor",
"HemoRecirculator",
"Power Recirculation Core",
"BrachiBlades",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "The Syndicate",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -745,21 +326,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Nanofiber Weave",
"Wired Reflexes",
"Speech Processor Implant",
"Neuroreceptor Management Implant",
"Nuoptimal Nootropic Injector Implant",
"Speech Enhancement",
"ADR-V1 Pheromone Gene",
"NeuroFlux Governor",
"Social Negotiation Assistant (S.N.A)",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Tian Di Hui",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },
@@ -767,20 +336,9 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"ctor": "Faction", "ctor": "Faction",
"data": { "data": {
"alreadyInvited": false, "alreadyInvited": false,
"augmentations": [
"Combat Rib I",
"Combat Rib II",
"Wired Reflexes",
"Speech Processor Implant",
"Neuralstimulator",
"Nuoptimal Nootropic Injector Implant",
"NeuroFlux Governor",
"DermaForce Particle Barrier",
],
"favor": 0, "favor": 0,
"isBanned": false, "isBanned": false,
"isMember": false, "isMember": false,
"name": "Volhaven",
"playerReputation": 0, "playerReputation": 0,
}, },
}, },