mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-27 11:27:04 +02:00
TYPESAFETY: FactionName (#644)
This commit is contained in:
+25
-17
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user