@@ -273,14 +265,14 @@ export function FactionsRoot(): React.ReactElement {
>
)}
- {rumors.length > 0 && (
+ {Player.factionRumors.size > 0 && (
<>
Rumors
- {rumors.map((faction) => (
-
+ {[...Player.factionRumors].map((factionName) => (
+
))}
>
diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts
index 95b66b378..526e92c9f 100644
--- a/src/PersonObjects/Player/PlayerObject.ts
+++ b/src/PersonObjects/Player/PlayerObject.ts
@@ -21,7 +21,7 @@ import { CompanyName, FactionName, JobName, LocationName } from "@enums";
import { HashManager } from "../../Hacknet/HashManager";
import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../../utils/JSONReviver";
-import { JSONMap } from "../../Types/Jsonable";
+import { JSONMap, JSONSet } from "../../Types/Jsonable";
import { cyrb53 } from "../../utils/StringHelperFunctions";
import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { CONSTANTS } from "../../Constants";
@@ -38,7 +38,7 @@ export class PlayerObject extends Person implements IPlayer {
currentServer = "";
factions: FactionName[] = [];
factionInvitations: FactionName[] = [];
- factionRumors: FactionName[] = [];
+ factionRumors = new JSONSet();
hacknetNodes: (HacknetNode | string)[] = []; // HacknetNode object or hostname of Hacknet Server
has4SData = false;
has4SDataTixApi = false;
diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts b/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts
index 89f6e7f73..99b3a5f77 100644
--- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts
+++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts
@@ -103,7 +103,7 @@ export function prestigeAugmentation(this: PlayerObject): void {
this.factions = [];
this.factionInvitations = [];
- this.factionRumors = [];
+ this.factionRumors.clear();
// Clear any pending invitation modals
InvitationEvent.emit(null);
@@ -170,37 +170,17 @@ export function prestigeSourceFile(this: PlayerObject): void {
}
export function receiveInvite(this: PlayerObject, factionName: FactionName): void {
- if (
- this.factionInvitations.includes(factionName) ||
- Factions[factionName].isMember ||
- Factions[factionName].isBanned
- ) {
- return;
- }
+ const faction = Factions[factionName];
+ if (faction.alreadyInvited || faction.isMember || faction.isBanned) return;
this.factionInvitations.push(factionName);
- if (this.factionRumors.includes(factionName)) {
- this.factionRumors.splice(this.factionRumors.indexOf(factionName), 1);
- }
- Factions[factionName].discovery = FactionDiscovery.known;
+ this.factionRumors.delete(factionName);
+ faction.discovery = FactionDiscovery.known;
}
-export function receiveRumor(
- this: PlayerObject,
- factionName: FactionName,
- discovery?: FactionDiscovery | undefined,
-): void {
- if (Factions[factionName].discovery == FactionDiscovery.unknown) {
- Factions[factionName].discovery = discovery || FactionDiscovery.rumored;
- }
- if (
- this.factionRumors.includes(factionName) ||
- this.factionInvitations.includes(factionName) ||
- Factions[factionName].isMember ||
- Factions[factionName].isBanned
- ) {
- return;
- }
- this.factionRumors.push(factionName);
+export function receiveRumor(this: PlayerObject, factionName: FactionName): void {
+ const faction = Factions[factionName];
+ if (this.factionRumors.has(factionName) || faction.isMember || faction.isBanned || faction.alreadyInvited) return;
+ this.factionRumors.add(factionName);
}
//Calculates skill level progress based on experience. The same formula will be used for every skill
@@ -630,15 +610,22 @@ export function reapplyAllSourceFiles(this: PlayerObject): void {
this.updateSkillLevels();
}
-/*************** Check for Faction Invitations *************/
-//This function sets the requirements to join a Faction. It checks whether the Player meets
-//those requirements and will return an array of all factions that the Player should
-//receive an invitation to
+/**
+ * Checks whether a player meets the requirements for joining each faction, and returns an array of all invitations the player should receive.
+ * Also handles receiving rumors for factions if the rumor requirements are met.
+ */
export function checkForFactionInvitations(this: PlayerObject): Faction[] {
const invitedFactions = [];
for (const faction of Object.values(Factions)) {
- if (faction.checkForInvite(this)) invitedFactions.push(faction);
- if (faction.checkForRumor(this)) this.receiveRumor(faction.name);
+ if (faction.isBanned) continue;
+ if (faction.isMember) continue;
+ if (faction.alreadyInvited) continue;
+ // Handle invites
+ const { inviteReqs, rumorReqs } = faction.getInfo();
+ if (inviteReqs.every((req) => req.isSatisfied(this))) invitedFactions.push(faction);
+ // Handle rumors
+ if (faction.discovery !== FactionDiscovery.unknown) continue;
+ if (rumorReqs.every((req) => req.isSatisfied(this))) this.receiveRumor(faction.name);
}
return invitedFactions;
}
diff --git a/src/SaveObject.ts b/src/SaveObject.ts
index 288621fbb..5e3e499de 100644
--- a/src/SaveObject.ts
+++ b/src/SaveObject.ts
@@ -716,7 +716,7 @@ function loadGame(saveString: string): boolean {
setPlayer(loadPlayer(saveObj.PlayerSave));
loadAllServers(saveObj.AllServersSave);
loadCompanies(saveObj.CompaniesSave);
- loadFactions(saveObj.FactionsSave);
+ loadFactions(saveObj.FactionsSave, Player);
if (Object.hasOwn(saveObj, "StaneksGiftSave")) {
loadStaneksGift(saveObj.StaneksGiftSave);
diff --git a/test/jest/__snapshots__/FullSave.test.ts.snap b/test/jest/__snapshots__/FullSave.test.ts.snap
index 43775f50c..1e06c2046 100644
--- a/test/jest/__snapshots__/FullSave.test.ts.snap
+++ b/test/jest/__snapshots__/FullSave.test.ts.snap
@@ -276,374 +276,272 @@ exports[`Check Save File Continuity FactionsSave continuity 1`] = `
"Aevum": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Bachman & Associates": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"BitRunners": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Blade Industries": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Bladeburners": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": true,
"playerReputation": 4000,
},
},
"Chongqing": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Church of the Machine God": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Clarke Incorporated": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"CyberSec": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 20,
- "isBanned": false,
- "isMember": true,
"playerReputation": 1000000,
},
},
"Daedalus": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"ECorp": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Four Sigma": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Fulcrum Secret Technologies": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Illuminati": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Ishima": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"KuaiGong International": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"MegaCorp": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"NWO": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Netburners": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"New Tokyo": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"NiteSec": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"OmniTek Incorporated": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Sector-12": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Shadows of Anarchy": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Silhouette": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Slum Snakes": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": true,
"playerReputation": 0,
},
},
"Speakers for the Dead": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Tetrads": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"The Black Hand": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"The Covenant": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"The Dark Army": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"The Syndicate": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Tian Di Hui": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
"Volhaven": {
"ctor": "Faction",
"data": {
- "alreadyInvited": false,
"discovery": "unknown",
"favor": 0,
- "isBanned": false,
- "isMember": false,
"playerReputation": 0,
},
},
@@ -1272,7 +1170,10 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = `
},
"exploits": [],
"factionInvitations": [],
- "factionRumors": [],
+ "factionRumors": {
+ "ctor": "JSONSet",
+ "data": [],
+ },
"factions": [
"Slum Snakes",
"CyberSec",