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
+6 -13
View File
@@ -23,7 +23,7 @@ import { BladeburnerConstants } from "./data/Constants";
import { formatExp, formatMoney, formatPercent, formatBigNumber, formatStamina } from "../ui/formatNumber";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { addOffset } from "../utils/helpers/addOffset";
import { Factions, factionExists } from "../Faction/Factions";
import { Factions } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Settings } from "../Settings/Settings";
@@ -1608,18 +1608,11 @@ export class Bladeburner {
this.maxRank = Math.max(this.rank, this.maxRank);
const bladeburnersFactionName = FactionName.Bladeburners;
if (factionExists(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) {
const favorBonus = 1 + bladeburnerFac.favor / 100;
bladeburnerFac.playerReputation +=
BladeburnerConstants.RankToFactionRepFactor * change * person.mults.faction_rep * favorBonus;
}
const bladeburnerFac = Factions[bladeburnersFactionName];
if (bladeburnerFac.isMember) {
const favorBonus = 1 + bladeburnerFac.favor / 100;
bladeburnerFac.playerReputation +=
BladeburnerConstants.RankToFactionRepFactor * change * person.mults.faction_rep * favorBonus;
}
// Gain skill points
+8 -17
View File
@@ -124,38 +124,29 @@ function getRandomProblemType(): string {
}
function getRandomReward(): ICodingContractReward {
const reward: ICodingContractReward = {
name: "",
type: getRandomInt(0, CodingContractRewardType.Money),
};
reward.type = sanitizeRewardType(reward.type);
const rewardType = sanitizeRewardType(getRandomInt(0, CodingContractRewardType.Money));
// Add additional information based on the reward type
const factionsThatAllowHacking = Player.factions.filter((fac) => Factions[fac].getInfo().offerHackingWork);
switch (reward.type) {
switch (rewardType) {
case CodingContractRewardType.FactionReputation: {
// Get a random faction that player is a part of. That
// faction must allow hacking contracts
const numFactions = factionsThatAllowHacking.length;
const randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)];
reward.name = randFaction;
break;
return { type: rewardType, name: randFaction };
}
case CodingContractRewardType.CompanyReputation: {
const allJobs = Object.keys(Player.jobs);
if (allJobs.length > 0) {
reward.name = allJobs[getRandomInt(0, allJobs.length - 1)];
} else {
reward.type = CodingContractRewardType.Money;
return { type: CodingContractRewardType.CompanyReputation, name: allJobs[getRandomInt(0, allJobs.length - 1)] };
}
break;
return { type: CodingContractRewardType.Money };
}
default:
break;
return { type: rewardType };
}
return reward;
}
function getRandomServer(): BaseServer {
@@ -182,7 +173,7 @@ function getRandomServer(): BaseServer {
function getRandomFilename(
server: BaseServer,
reward: ICodingContractReward = { name: "", type: 0 },
reward: ICodingContractReward = { type: CodingContractRewardType.Money },
): ContractFilePath {
let contractFn = `contract-${getRandomInt(0, 1e6)}`;
@@ -197,7 +188,7 @@ function getRandomFilename(
contractFn = `contract-${getRandomInt(0, 1e6)}`;
}
if (reward.name) {
if ("name" in reward) {
// Only alphanumeric characters in the reward name.
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 { 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 */
export interface ICodingContractReward {
/* Name of Company/Faction name for reward, if applicable */
name?: string;
type: CodingContractRewardType;
}
export type ICodingContractReward =
| {
type: CodingContractRewardType.Money;
}
| {
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.
@@ -1,17 +1,17 @@
import type { FactionName } from "@enums";
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 * as corpConstants from "../../data/Constants";
import { formatReputation } from "../../../ui/formatNumber";
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal";
import { Player } from "@player";
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 Box from "@mui/material/Box";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { getEnumHelper } from "../../../utils/EnumHelper";
interface IProps {
open: boolean;
@@ -19,7 +19,7 @@ interface IProps {
}
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();
if (!info.offersWork()) return false;
if (Player.hasGangWith(name)) return false;
@@ -27,10 +27,11 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
});
const corp = useCorporation();
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;
function changeFaction(event: SelectChangeEvent): void {
if (!getEnumHelper("FactionName").isMember(event.target.value)) return;
setSelectedFaction(event.target.value);
}
@@ -52,6 +53,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
}
function bribe(money: number): void {
if (!selectedFaction) return;
const fac = Factions[selectedFaction];
if (disabled) return;
const rep = repGain(money);
@@ -69,7 +71,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
<Box display="flex" alignItems="center">
<Typography>Faction:</Typography>
<Select value={selectedFaction} onChange={changeFaction}>
{factions.map((name: string) => {
{factions.map((name) => {
const info = Factions[name].getInfo();
if (!info.offersWork()) 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 { Stats } from "./DevMenu/ui/Stats";
import { Factions } from "./DevMenu/ui/Factions";
import { FactionsDev } from "./DevMenu/ui/FactionsDev";
import { Augmentations } from "./DevMenu/ui/Augmentations";
import { SourceFiles } from "./DevMenu/ui/SourceFiles";
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>
<General />
<Stats />
<Factions />
<FactionsDev />
<Augmentations />
<SourceFiles />
<Programs />
@@ -1,7 +1,4 @@
import React, { useState } from "react";
import { Player } from "@player";
import { FactionName } from "@enums";
import {
Accordion,
AccordionSummary,
@@ -11,27 +8,33 @@ import {
IconButton,
InputLabel,
MenuItem,
Select,
SelectChangeEvent,
Typography,
} from "@mui/material";
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 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;
export function Factions(): React.ReactElement {
const [faction, setFaction] = useState(FactionName.Illuminati as string);
export function FactionsDev(): React.ReactElement {
const [factionName, setFactionName] = useState(FactionName.Illuminati);
function setFactionDropdown(event: SelectChangeEvent): void {
setFaction(event.target.value);
if (!getEnumHelper("FactionName").isMember(event.target.value)) return;
setFactionName(event.target.value);
}
function receiveInvite(): void {
Player.receiveInvite(faction);
Player.receiveInvite(factionName);
}
function receiveAllInvites(): void {
@@ -40,57 +43,53 @@ export function Factions(): React.ReactElement {
function modifyFactionRep(modifier: number): (x: number) => void {
return function (reputation: number): void {
const fac = AllFaction[faction];
if (fac != null && !isNaN(reputation)) {
const fac = Factions[factionName];
if (!isNaN(reputation)) {
fac.playerReputation += reputation * modifier;
}
};
}
function resetFactionRep(): void {
const fac = AllFaction[faction];
if (fac != null) {
fac.playerReputation = 0;
}
const fac = Factions[factionName];
fac.playerReputation = 0;
}
function modifyFactionFavor(modifier: number): (x: number) => void {
return function (favor: number): void {
const fac = AllFaction[faction];
if (fac != null && !isNaN(favor)) {
const fac = Factions[factionName];
if (!isNaN(favor)) {
fac.favor += favor * modifier;
}
};
}
function resetFactionFavor(): void {
const fac = AllFaction[faction];
if (fac != null) {
fac.favor = 0;
}
const fac = Factions[factionName];
fac.favor = 0;
}
function tonsOfRep(): void {
for (const i of Object.keys(AllFaction)) {
AllFaction[i].playerReputation = bigNumber;
for (const faction of getRecordValues(Factions)) {
faction.playerReputation = bigNumber;
}
}
function resetAllRep(): void {
for (const i of Object.keys(AllFaction)) {
AllFaction[i].playerReputation = 0;
for (const faction of getRecordValues(Factions)) {
faction.playerReputation = 0;
}
}
function tonsOfFactionFavor(): void {
for (const i of Object.keys(AllFaction)) {
AllFaction[i].favor = bigNumber;
for (const faction of getRecordValues(Factions)) {
faction.favor = bigNumber;
}
}
function resetAllFactionFavor(): void {
for (const i of Object.keys(AllFaction)) {
AllFaction[i].favor = 0;
for (const faction of getRecordValues(Factions)) {
faction.favor = 0;
}
}
@@ -113,7 +112,7 @@ export function Factions(): React.ReactElement {
labelId="factions-select"
id="factions-dropdown"
onChange={setFactionDropdown}
value={faction}
value={factionName}
startAdornment={
<>
<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}>
{faction.name}
</MenuItem>
+21 -10
View File
@@ -1,24 +1,31 @@
import React, { useEffect, useState } from "react";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import {
Accordion,
AccordionSummary,
AccordionDetails,
Button,
MenuItem,
Select,
SelectChangeEvent,
TextField,
Typography,
} from "@mui/material";
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 { FactionName } from "@enums";
import { Money } from "../../ui/React/Money";
import { Router } from "../../ui/GameRoot";
import { MenuItem, SelectChangeEvent, TextField, Select } from "@mui/material";
import { Bladeburner } from "../../Bladeburner/Bladeburner";
import { GangConstants } from "../../Gang/data/Constants";
import { FactionName } from "@enums";
import { checkForMessagesToSend } from "../../Message/MessageHelpers";
import { ThemeEvents } from "../../Themes/ui/Theme";
import { getEnumHelper } from "../../utils/EnumHelper";
export function General(): React.ReactElement {
const [error, setError] = useState(false);
const [corporationName, setCorporationName] = useState("");
const [gangFaction, setGangFaction] = useState("Slum Snakes");
const [gangFaction, setGangFaction] = useState(FactionName.SlumSnakes);
const [devMoney, setDevMoney] = useState(0);
// 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
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
const checkMessages = () => checkForMessagesToSend();
+25 -17
View File
@@ -1,8 +1,8 @@
import type { AugmentationName } from "@enums";
import { AugmentationName, FactionName } from "@enums";
import { FactionInfo, FactionInfos } from "./FactionInfo";
import { favorToRep, repToFavor } from "./formulas/favor";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { getEnumHelper } from "../utils/EnumHelper";
import { getKeyList } from "../utils/helpers/getKeyList";
export class Faction {
/**
@@ -24,12 +24,12 @@ export class Faction {
isMember = false;
/** Name of faction */
name = "";
name: FactionName;
/** Amount of reputation player has with this faction */
playerReputation = 0;
constructor(name = "") {
constructor(name = FactionName.Sector12) {
this.name = name;
}
@@ -44,11 +44,24 @@ export class Faction {
return info;
}
gainFavor(): void {
if (this.favor == null) {
this.favor = 0;
}
prestigeSourceFile() {
// Reset favor, reputation, and flags
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();
// 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]
@@ -62,21 +75,16 @@ export class Faction {
return newFavor - this.favor;
}
static savedKeys = getKeyList(Faction, { removedKeys: ["augmentations", "name"] });
/** Serialize the current object to a JSON save state. */
toJSON(): IReviverValue {
return Generic_toJSON("Faction", this);
return Generic_toJSON("Faction", this, Faction.savedKeys);
}
/** Initializes a Faction object from a JSON save state. */
static fromJSON(value: IReviverValue): Faction {
const faction = Generic_fromJSON(Faction, value.data);
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;
return Generic_fromJSON(Faction, value.data, Faction.savedKeys);
}
}
+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 { Augmentation } from "../Augmentation/Augmentation";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationName, FactionName } from "@enums";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Faction } from "./Faction";
import { Factions } from "./Factions";
import { Player } from "@player";
import { Factions } from "./Factions";
import { Settings } from "../Settings/Settings";
import {
getHackingWorkRepGain,
@@ -19,6 +20,7 @@ import { InvitationEvent } from "./ui/InvitationModal";
import { SFC32RNG } from "../Casino/RNG";
import { isFactionWork } from "../Work/FactionWork";
import { getAugCost } from "../Augmentation/AugmentationHelpers";
import { createEnumKeyedRecord, getRecordKeys } from "../Types/Record";
export function inviteToFaction(faction: Faction): void {
Player.receiveInvite(faction.name);
@@ -32,8 +34,9 @@ export function joinFaction(faction: Faction): void {
if (faction.isMember) return;
faction.isMember = true;
Player.factions.push(faction.name);
const allFactions = Object.values(FactionName).map((faction) => faction as string);
Player.factions.sort((a, b) => allFactions.indexOf(a) - allFactions.indexOf(b));
let i = 0;
const factionIndexes = createEnumKeyedRecord(FactionName, (__) => i++);
Player.factions.sort((a, b) => factionIndexes[a] - factionIndexes[b]);
const factionInfo = faction.getInfo();
//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 {
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 (!Object.hasOwn(Factions, name)) continue;
const faction = Factions[name];
if (!faction.isMember) continue;
// No passive rep for special factions
+3 -3
View File
@@ -7,7 +7,7 @@ import { Typography } from "@mui/material";
interface FactionInfoParams {
infoText?: JSX.Element;
enemies?: string[];
enemies?: FactionName[];
offerHackingWork?: boolean;
offerFieldWork?: 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 */
export class FactionInfo {
/** 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. */
infoText: JSX.Element;
@@ -60,7 +60,7 @@ export class FactionInfo {
}
/** A map of all factions and associated info to them. */
export const FactionInfos: Record<string, FactionInfo> = {
export const FactionInfos: Record<FactionName, FactionInfo> = {
// Endgame
[FactionName.Illuminati]: new FactionInfo({
infoText: (
+32 -53
View File
@@ -2,63 +2,42 @@
* Initialization and manipulation of the Factions object, which stores data
* about all Factions in the game
*/
import { FactionName } from "@enums";
import { Faction } from "./Faction";
import { FactionInfos } from "./FactionInfo";
import { Reviver } from "../utils/JSONReviver";
import { getRecordValues } from "../Types/Record";
import { Augmentations, initCircadianModulator } from "../Augmentation/Augmentations";
import { Reviver, assertLoadingType } from "../utils/JSONReviver";
import { createEnumKeyedRecord, getRecordValues } from "../Types/Record";
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));
// Add the associated augs to every faction
for (const aug of getRecordValues(Augmentations)) {
for (const factionName of aug.factions) {
const faction = Factions[factionName];
faction.augmentations.push(aug.name);
}
}
export function loadFactions(saveString: string): void {
Factions = JSON.parse(saveString, Reviver);
// 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];
}
// The only information that should be loaded from player save is
const loadedFactions = JSON.parse(saveString, Reviver) as unknown;
// This loading method allows invalid data in player save, but just ignores anything invalid
if (!loadedFactions) return;
if (typeof loadedFactions !== "object") return;
for (const [loadedFactionName, loadedFaction] of Object.entries(loadedFactions) as [string, unknown][]) {
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;
}
}
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) {
const faction = Factions[factionName];
if (!faction) {
console.error(`Faction ${factionName} did not exist while adding augs to factions`);
continue;
}
faction.augmentations.push(aug.name);
}
}
}
//Resets a faction during (re-)initialization. Saves the favor in the new
//Faction object and deletes the old Faction Object from "Factions". Then
//reinserts the new Faction object
function resetFaction(newFactionObject: Faction): void {
const factionName: string = newFactionObject.name;
if (factionExists(factionName)) {
newFactionObject.favor = Factions[factionName].favor;
delete Factions[factionName];
}
AddToFactions(newFactionObject);
}
+2 -2
View File
@@ -11,7 +11,7 @@ import { FactionName } from "@enums";
interface IProps {
open: boolean;
onClose: () => void;
facName: string;
facName: FactionName;
}
/** 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.";
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 {
+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 { Box, Button, Container, Paper, Tooltip, Typography, useTheme } from "@mui/material";
import React, { useEffect } from "react";
import { Player } from "@player";
import { Settings } from "../../Settings/Settings";
import { formatFavor, formatReputation } from "../../ui/formatNumber";
import { Router } from "../../ui/GameRoot";
import { FactionName } from "@enums";
import { Faction } from "../Faction";
import { getFactionAugmentationsFiltered, joinFaction } from "../FactionHelpers";
import { Factions } from "../Factions";
import { useRerender } from "../../ui/React/hooks";
@@ -59,7 +60,7 @@ const FactionElement = (props: FactionElementProps): React.ReactElement => {
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;
joinFaction(Factions[faction]);
props.rerender();
+3 -2
View File
@@ -23,9 +23,10 @@ import { GangMember } from "./GangMember";
import { WorkerScript } from "../Netscript/WorkerScript";
import { Player } from "@player";
import { PowerMultiplier } from "./data/power";
import { FactionName } from "@enums";
export class Gang {
facName: string;
facName: FactionName;
members: GangMember[];
wanted: number;
respect: number;
@@ -48,7 +49,7 @@ export class Gang {
notifyMemberDeath: boolean;
constructor(facName = "", hacking = false) {
constructor(facName = FactionName.SlumSnakes, hacking = false) {
this.facName = facName;
this.members = [];
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 { Box, Button, MenuItem, Paper, Select, SelectChangeEvent, Typography } from "@mui/material";
import { Player } from "@player";
import { FactionName } from "@enums";
import { inviteToFaction } from "../../Faction/FactionHelpers";
import { Factions } from "../../Faction/Factions";
import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player";
import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation";
import { formatNumberNoSuffix } from "../../ui/formatNumber";
@@ -14,6 +16,7 @@ import {
calculateSellInformationCashReward,
calculateTradeInformationRepReward,
} from "../formulas/victory";
import { getEnumHelper } from "../../utils/EnumHelper";
interface IProps {
StartingDifficulty: number;
@@ -23,7 +26,7 @@ interface IProps {
}
export function Victory(props: IProps): React.ReactElement {
const [faction, setFaction] = useState("none");
const [factionName, setFactionName] = useState("none");
function quitInfiltration(): void {
handleInfiltrators();
@@ -43,13 +46,13 @@ export function Victory(props: IProps): React.ReactElement {
}
function trade(): void {
if (faction === "none") return;
Factions[faction].playerReputation += repGain;
if (!getEnumHelper("FactionName").isMember(factionName)) return;
Factions[factionName].playerReputation += repGain;
quitInfiltration();
}
function changeDropdown(event: SelectChangeEvent): void {
setFaction(event.target.value);
setFactionName(event.target.value);
}
function handleInfiltrators(): void {
@@ -75,7 +78,7 @@ export function Victory(props: IProps): React.ReactElement {
</Typography>
<Box sx={{ width: "fit-content" }}>
<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"}>
{"none"}
</MenuItem>
+5 -5
View File
@@ -3,15 +3,15 @@ import { Player } from "@player";
import { Factions } from "../Faction/Factions";
import { Faction } from "../Faction/Faction";
import { GetServer } from "../Server/AllServers";
import { FactionName } from "@enums";
import { AugmentationName, FactionName } from "@enums";
import { Server } from "../Server/Server";
function allFactionAugs(f: Faction): boolean {
const factionAugs = f.augmentations.slice().filter((aug) => aug !== "NeuroFlux Governor");
for (const factionAug of factionAugs) {
function allFactionAugs(faction: Faction): boolean {
for (const factionAugName of faction.augmentations) {
if (factionAugName === AugmentationName.NeuroFluxGovernor) continue;
if (
!Player.augmentations.some((aug) => {
return aug.name == factionAug;
return aug.name == factionAugName;
})
)
return false;
+3 -4
View File
@@ -51,7 +51,7 @@ import {
} from "../Corporation/Actions";
import { CorpUnlocks } from "../Corporation/data/CorporationUnlocks";
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 * as corpConstants from "../Corporation/data/Constants";
import { ResearchMap } from "../Corporation/ResearchMap";
@@ -174,8 +174,7 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
return division.researched.has(researchName);
}
function bribe(factionName: string, amountCash: number): boolean {
if (!player.factions.includes(factionName)) throw new Error("Invalid faction name");
function bribe(factionName: FactionName, amountCash: number): boolean {
if (isNaN(amountCash) || amountCash < 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) => {
checkAccess(ctx);
const factionName = helpers.string(ctx, "factionName", _factionName);
const factionName = getEnumHelper("FactionName").nsGetMember(ctx, _factionName);
const amountCash = helpers.number(ctx, "amountCash", _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 { GangConstants } from "../Gang/data/Constants";
import { Player } from "@player";
import { Gang } from "../Gang/Gang";
import { AllGangs } from "../Gang/AllGangs";
import { GangMemberTasks } from "../Gang/GangMemberTasks";
import { GangMemberUpgrades } from "../Gang/GangMemberUpgrades";
import { GangMember } from "../Gang/GangMember";
import { GangMemberTask } from "../Gang/GangMemberTask";
import { helpers } from "../Netscript/NetscriptHelpers";
import { Gang as IGang, EquipmentStats, GangOtherInfoObject } from "@nsdefs";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { getEnumHelper } from "../utils/EnumHelper";
export function NetscriptGang(): InternalAPI<IGang> {
/** Functions as an API check and also returns the gang object */
@@ -36,9 +37,7 @@ export function NetscriptGang(): InternalAPI<IGang> {
return {
createGang: (ctx) => (_faction) => {
const faction = helpers.string(ctx, "faction", _faction);
// this list is copied from Faction/ui/Root.tsx
const faction = getEnumHelper("FactionName").nsGetMember(ctx, _faction);
if (!Player.canAccessGang() || !GangConstants.Names.includes(faction)) return false;
if (Player.gang) 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 { Company } from "../Company/Company";
import type { Faction } from "../Faction/Faction";
import { Player } from "@player";
import {
@@ -33,7 +32,7 @@ import { formatMoney, formatRam, formatReputation } from "../ui/formatNumber";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Companies } from "../Company/Companies";
import { companiesMetadata } from "../Company/data/CompaniesMetadata";
import { Factions, factionExists } from "../Faction/Factions";
import { Factions } from "../Faction/Factions";
import { helpers } from "../Netscript/NetscriptHelpers";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { getServerOnNetwork } from "../Server/ServerHelpers";
@@ -59,14 +58,6 @@ import { ScriptFilePath, resolveScriptFilePath } from "../Paths/ScriptFilePath";
import { root } from "../Paths/Directory";
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 company = Companies[name];
if (!company) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company name: '${name}'`);
@@ -112,9 +103,8 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
},
getAugmentationsFromFaction: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const faction = getFaction(ctx, facName);
const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const faction = Factions[facName];
return getFactionAugmentationsFiltered(faction);
},
getAugmentationPrereq: (ctx) => (_augName) => {
@@ -149,19 +139,19 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
},
purchaseAugmentation: (ctx) => (_facName, _augName) => {
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 fac = getFaction(ctx, facName);
const fac = Factions[facName];
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`);
return false;
}
if (!augs.includes(augName)) {
if (!factionAugs.includes(augName)) {
helpers.log(ctx, () => `Faction '${facName}' does not have the '${augName}' augmentation.`);
return false;
}
@@ -867,8 +857,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
},
joinFaction: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
getFaction(ctx, facName);
const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
if (!Player.factionInvitations.includes(facName)) {
helpers.log(ctx, () => `You have not been invited by faction '${facName}'`);
@@ -892,10 +881,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
(ctx) =>
(_facName, _type, _focus = true) => {
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 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 (Player.gang && faction.name === Player.getGangFaction().name) {
@@ -987,27 +976,27 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
},
getFactionRep: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const faction = getFaction(ctx, facName);
const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const faction = Factions[facName];
return faction.playerReputation;
},
getFactionFavor: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const faction = getFaction(ctx, facName);
const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const faction = Factions[facName];
return faction.favor;
},
getFactionFavorGain: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const faction = getFaction(ctx, facName);
const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const faction = Factions[facName];
return faction.getFavorGain();
},
donateToFaction: (ctx) => (_facName, _amt) => {
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 faction = getFaction(ctx, facName);
const faction = Factions[facName];
if (!Player.factions.includes(faction.name)) {
helpers.log(ctx, () => `You can't donate to '${facName}' because you aren't a member`);
return false;
+3 -3
View File
@@ -17,7 +17,7 @@ import * as serverMethods from "./PlayerObjectServerMethods";
import * as workMethods from "./PlayerObjectWorkMethods";
import { setPlayer } from "../../Player";
import { LocationName } from "@enums";
import { FactionName, LocationName } from "@enums";
import { HashManager } from "../../Hacknet/HashManager";
import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
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;
bladeburner: Bladeburner | null = null;
currentServer = "";
factions: string[] = [];
factionInvitations: string[] = [];
factions: FactionName[] = [];
factionInvitations: FactionName[] = [];
hacknetNodes: (HacknetNode | string)[] = []; // HacknetNode object or hostname of Hacknet Server
has4SData = 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 { Faction } from "../../Faction/Faction";
import { Gang } from "../../Gang/Gang";
import { GangConstants } from "../../Gang/data/Constants";
import { isFactionWork } from "../../Work/FactionWork";
import type { PlayerObject } from "./PlayerObject";
export function canAccessGang(this: PlayerObject): boolean {
if (this.bitNodeN === 2) {
return true;
@@ -36,12 +37,12 @@ export function getGangName(this: PlayerObject): string {
return gang ? gang.facName : "";
}
export function hasGangWith(this: PlayerObject, facName: string): boolean {
export function hasGangWith(this: PlayerObject, facName: FactionName): boolean {
const gang = this.gang;
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
if (isFactionWork(this.currentWork) && this.currentWork.factionName === factionName) this.finishWork(false);
@@ -158,7 +158,7 @@ export function prestigeSourceFile(this: PlayerObject): void {
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)) {
return;
}
@@ -1099,34 +1099,34 @@ export function gainCodingContractReward(
reward: ICodingContractReward | null,
difficulty = 1,
): string {
if (reward == null || reward.type == null) {
return `No reward for this contract`;
}
if (!reward) return `No reward for this contract`;
/* eslint-disable no-case-declarations */
switch (reward.type) {
case CodingContractRewardType.FactionReputation:
if (reward.name == null || !Factions[reward.name]) {
// If no/invalid faction was designated, just give rewards to all factions
reward.type = CodingContractRewardType.FactionReputationAll;
return this.gainCodingContractReward(reward);
case CodingContractRewardType.FactionReputation: {
if (!Factions[reward.name]) {
return this.gainCodingContractReward({ type: CodingContractRewardType.FactionReputationAll });
}
const repGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty;
Factions[reward.name].playerReputation += repGain;
return `Gained ${repGain} faction reputation for ${reward.name}`;
case CodingContractRewardType.FactionReputationAll:
}
case CodingContractRewardType.FactionReputationAll: {
const totalGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty;
// 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) => {
return !specialFactions.includes(f);
});
// If the player was only part of the special factions, we'll just give money
if (factions.length == 0) {
reward.type = CodingContractRewardType.Money;
return this.gainCodingContractReward(reward, difficulty);
return this.gainCodingContractReward({ type: CodingContractRewardType.Money }, difficulty);
}
const gainPerFaction = Math.floor(totalGain / factions.length);
@@ -1135,11 +1135,11 @@ export function gainCodingContractReward(
Factions[facName].playerReputation += gainPerFaction;
}
return `Gained ${gainPerFaction} reputation for each of the following factions: ${factions.join(", ")}`;
}
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
reward.type = CodingContractRewardType.FactionReputationAll;
return this.gainCodingContractReward(reward);
return this.gainCodingContractReward({ type: CodingContractRewardType.FactionReputationAll });
}
const repGain = CONSTANTS.CodingContractBaseCompanyRepGain * difficulty;
Companies[reward.name].playerReputation += repGain;
+18 -25
View File
@@ -292,38 +292,31 @@ export class Sleeve extends Person implements SleevePerson {
return true;
}
/**
* Start work for one of the player's factions
* Returns boolean indicating success
*/
workForFaction(factionName: string, workType: string): boolean {
/** TODO 2.4: Make this take in type correct data */
workForFaction(_factionName: string, _workType: string): boolean {
const factionName = getEnumHelper("FactionName").fuzzyGetMember(_factionName);
if (!factionName) return false;
const faction = Factions[factionName];
if (factionName === "" || !faction || !Player.factions.includes(factionName)) {
return false;
}
const workType = getEnumHelper("FactionWorkType").fuzzyGetMember(_workType);
if (!workType) return false;
const factionInfo = faction.getInfo();
// Set type of work (hacking/field/security), and the experience gains
const sanitizedWorkType = workType.toLowerCase();
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;
factionWorkType = FactionWorkType.field;
} else if (sanitizedWorkType.includes("security")) {
if (!factionInfo.offerSecurityWork) return false;
factionWorkType = FactionWorkType.security;
} else {
return false;
switch (workType) {
case FactionWorkType.field:
if (!factionInfo.offerFieldWork) return false;
break;
case FactionWorkType.hacking:
if (!factionInfo.offerHackingWork) return false;
break;
case FactionWorkType.security:
if (!factionInfo.offerSecurityWork) return false;
break;
}
this.startWork(
new SleeveFactionWork({
factionWorkType: factionWorkType,
factionName: faction.name,
factionWorkType: workType,
factionName: factionName,
}),
);
@@ -7,11 +7,11 @@ import { Factions } from "../../../Faction/Factions";
import { calculateFactionExp, calculateFactionRep } from "../../../Work/Formulas";
import { Faction } from "../../../Faction/Faction";
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { findEnumMember } from "../../../utils/helpers/enum";
import { getEnumHelper } from "../../../utils/EnumHelper";
interface SleeveFactionWorkParams {
factionWorkType: FactionWorkType;
factionName: string;
factionName: FactionName;
}
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 {
type: SleeveWorkType.FACTION = SleeveWorkType.FACTION;
factionWorkType: FactionWorkType;
factionName: string;
factionName: FactionName;
constructor(params?: SleeveFactionWorkParams) {
super();
@@ -67,8 +67,8 @@ export class SleeveFactionWork extends SleeveWorkClass {
/** Initializes a FactionWork object from a JSON save state. */
static fromJSON(value: IReviverValue): SleeveFactionWork {
const factionWork = Generic_fromJSON(SleeveFactionWork, value.data);
factionWork.factionWorkType =
findEnumMember(FactionWorkType, factionWork.factionWorkType) ?? FactionWorkType.hacking;
factionWork.factionWorkType = getEnumHelper("FactionWorkType").fuzzyGetMember(factionWork.factionWorkType, true);
factionWork.factionName = getEnumHelper("FactionName").fuzzyGetMember(factionWork.factionName, true);
return factionWork;
}
}
+2 -3
View File
@@ -137,10 +137,9 @@ const tasks: {
return {
first: factions,
second: (s1: string) => {
second: (s1) => {
if (!getEnumHelper("FactionName").isMember(s1)) return ["------"];
const faction = Factions[s1];
if (!faction) return ["------"];
const facInfo = faction.getInfo();
const options: string[] = [];
if (facInfo.offerHackingWork) {
+9 -7
View File
@@ -2,7 +2,7 @@ import { AugmentationName, CityName, CompletedProgramName, FactionName, Literatu
import { initBitNodeMultipliers } from "./BitNode/BitNode";
import { Companies, initCompanies } from "./Company/Companies";
import { resetIndustryResearchTrees } from "./Corporation/data/IndustryData";
import { Factions, initFactions } from "./Faction/Factions";
import { Factions } from "./Faction/Factions";
import { joinFaction } from "./Faction/FactionHelpers";
import { updateHashManagerCapacity } from "./Hacknet/HacknetHelpers";
import { prestigeWorkerScripts } from "./NetscriptWorker";
@@ -23,6 +23,7 @@ import { ProgramsSeen } from "./Programs/ui/ProgramsRoot";
import { InvitationsSeen } from "./Faction/ui/FactionsRoot";
import { CONSTANTS } from "./Constants";
import { LogBoxClearEvents } from "./ui/React/LogBoxManager";
import { initCircadianModulator } from "./Augmentation/Augmentations";
const BitNode8StartingMoney = 250e6;
function delayedDialog(message: string) {
@@ -71,7 +72,7 @@ export function prestigeAugmentation(): void {
// Gain favor for Companies and Factions
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.
if (Terminal.action !== null) {
@@ -80,10 +81,11 @@ export function prestigeAugmentation(): void {
Terminal.clear();
LogBoxClearEvents.emit();
// Re-initialize things - This will update any changes
initFactions(); // Factions must be initialized before augmentations
// Recalculate the bonus for circadian modulator aug
initCircadianModulator();
Player.factionInvitations = Player.factionInvitations.concat(maintainMembership);
for (const factionName of maintainMembership) Factions[factionName].alreadyInvited = true;
Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles();
Player.hp.current = Player.hp.max;
@@ -193,7 +195,7 @@ export function prestigeSourceFile(isFlume: boolean): void {
// Reset favor for Companies and Factions
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
if (Terminal.action !== null) {
@@ -208,8 +210,8 @@ export function prestigeSourceFile(isFlume: boolean): void {
});
}
// Re-initialize things - This will update any changes
initFactions(); // Factions must be initialized before augmentations
initCircadianModulator();
Player.reapplyAllAugmentations();
Player.reapplyAllSourceFiles();
initCompanies();
+8 -9
View File
@@ -1,28 +1,29 @@
import type { Faction } from "../Faction/Faction";
import React from "react";
import { Work, WorkType } from "./Work";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { Player } from "@player";
import { AugmentationName, FactionName, FactionWorkType } from "@enums";
import { Factions } from "../Faction/Factions";
import { Faction } from "../Faction/Faction";
import { applyWorkStats, scaleWorkStats, WorkStats } from "./WorkStats";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reputation } from "../ui/React/Reputation";
import { CONSTANTS } from "../Constants";
import { calculateFactionExp, calculateFactionRep } from "./Formulas";
import { findEnumMember } from "../utils/helpers/enum";
import { getEnumHelper } from "../utils/EnumHelper";
interface FactionWorkParams {
singularity: boolean;
factionWorkType: FactionWorkType;
faction: string;
faction: FactionName;
}
export const isFactionWork = (w: Work | null): w is FactionWork => w !== null && w.type === WorkType.FACTION;
export class FactionWork extends Work {
factionWorkType: FactionWorkType;
factionName: string;
factionName: FactionName;
constructor(params?: FactionWorkParams) {
super(WorkType.FACTION, params?.singularity ?? true);
@@ -31,9 +32,7 @@ export class FactionWork extends Work {
}
getFaction(): Faction {
const f = Factions[this.factionName];
if (!f) throw new Error(`Faction work started with invalid / unknown faction: '${this.factionName}'`);
return f;
return Factions[this.factionName];
}
getReputationRate(): number {
@@ -92,8 +91,8 @@ export class FactionWork extends Work {
/** Initializes a FactionWork object from a JSON save state. */
static fromJSON(value: IReviverValue): FactionWork {
const factionWork = Generic_fromJSON(FactionWork, value.data);
factionWork.factionWorkType =
findEnumMember(FactionWorkType, factionWork.factionWorkType) ?? FactionWorkType.hacking;
factionWork.factionWorkType = getEnumHelper("FactionWorkType").fuzzyGetMember(factionWork.factionWorkType, true);
factionWork.factionName = getEnumHelper("FactionName").fuzzyGetMember(factionWork.factionName, true);
return factionWork;
}
}
+1 -2
View File
@@ -5,7 +5,7 @@ import { initSourceFiles } from "./SourceFile/SourceFiles";
import { generateRandomContract } from "./CodingContractGenerator";
import { initCompanies } from "./Company/Companies";
import { CONSTANTS } from "./Constants";
import { Factions, initFactions } from "./Faction/Factions";
import { Factions } from "./Faction/Factions";
import { staneksGift } from "./CotMG/Helper";
import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers";
import { Router } from "./ui/GameRoot";
@@ -375,7 +375,6 @@ const Engine: {
Player.init();
initForeignServers(Player.getHomeComputer());
initCompanies();
initFactions();
Player.reapplyAllAugmentations();
// Start interactive tutorial
+6
View File
@@ -1,4 +1,6 @@
/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */
import type { Unknownify } from "../types";
import { ObjectValidator, validateObject } from "./Validator";
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;
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> {}