Hacknet server achievements grant associated hacknet node achieve

fixes #1905

Added a new property to the Achievement interface to contain a list
of additional achievements that should be granted when an achievement's
conditional passes.

Right now this is only used to unlock the 4 hacknet node achieves
when the corresponding hacknet server achievement is awarded.

This branch also changes the calculateAchievements function so it
only runs the conditionals for achievements the player doesn't have,
instead of checking all of them and then filtering out the unowned.
This commit is contained in:
Russell Stringer 2022-01-11 13:41:08 -05:00
parent c941ec2ba6
commit f59a145965

@ -34,6 +34,7 @@ export interface Achievement {
Secret?: boolean; Secret?: boolean;
Condition: () => boolean; Condition: () => boolean;
Visible?: () => boolean; Visible?: () => boolean;
AdditionalUnlock?: string[]; // IDs of achievements that should be awarded when awarding this one
} }
export interface PlayerAchievement { export interface PlayerAchievement {
@ -510,12 +511,14 @@ export const achievements: IMap<Achievement> = {
Icon: "HASHNET", Icon: "HASHNET",
Visible: () => hasAccessToSF(Player, 9), Visible: () => hasAccessToSF(Player, 9),
Condition: () => hasHacknetServers(Player) && Player.hacknetNodes.length > 0, Condition: () => hasHacknetServers(Player) && Player.hacknetNodes.length > 0,
AdditionalUnlock: [achievementData.FIRST_HACKNET_NODE.ID],
}, },
ALL_HACKNET_SERVER: { ALL_HACKNET_SERVER: {
...achievementData["ALL_HACKNET_SERVER"], ...achievementData["ALL_HACKNET_SERVER"],
Icon: "HASHNETALL", Icon: "HASHNETALL",
Visible: () => hasAccessToSF(Player, 9), Visible: () => hasAccessToSF(Player, 9),
Condition: () => hasHacknetServers(Player) && Player.hacknetNodes.length === HacknetServerConstants.MaxServers, Condition: () => hasHacknetServers(Player) && Player.hacknetNodes.length === HacknetServerConstants.MaxServers,
AdditionalUnlock: [achievementData["30_HACKNET_NODE"].ID],
}, },
MAX_HACKNET_SERVER: { MAX_HACKNET_SERVER: {
...achievementData["MAX_HACKNET_SERVER"], ...achievementData["MAX_HACKNET_SERVER"],
@ -537,12 +540,14 @@ export const achievements: IMap<Achievement> = {
} }
return false; return false;
}, },
AdditionalUnlock: [achievementData.MAX_HACKNET_NODE.ID],
}, },
HACKNET_SERVER_1B: { HACKNET_SERVER_1B: {
...achievementData["HACKNET_SERVER_1B"], ...achievementData["HACKNET_SERVER_1B"],
Icon: "HASHNETMONEY", Icon: "HASHNETMONEY",
Visible: () => hasAccessToSF(Player, 9), Visible: () => hasAccessToSF(Player, 9),
Condition: () => hasHacknetServers(Player) && Player.moneySourceB.hacknet >= 1e9, Condition: () => hasHacknetServers(Player) && Player.moneySourceB.hacknet >= 1e9,
AdditionalUnlock: [achievementData.HACKNET_NODE_10M.ID],
}, },
MAX_CACHE: { MAX_CACHE: {
...achievementData["MAX_CACHE"], ...achievementData["MAX_CACHE"],
@ -763,7 +768,8 @@ export function calculateAchievements(): void {
const missingAchievements = Object.values(achievements) const missingAchievements = Object.values(achievements)
.filter((a) => !playerAchievements.includes(a.ID) && a.Condition()) .filter((a) => !playerAchievements.includes(a.ID) && a.Condition())
.map((a) => a.ID); // callback returns array of achievement id and id of any in the additional list, flatmap means we have only a 1D array
.flatMap((a) => [a.ID, ...(a.AdditionalUnlock || [])]);
for (const id of missingAchievements) { for (const id of missingAchievements) {
Player.giveAchievement(id); Player.giveAchievement(id);