Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACHIEVEMENTS: Support achievements with no matching Steam achievement #1508

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from
16 changes: 16 additions & 0 deletions assets/Steam/achievements/pack-for-web-mac.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

BASEDIR=$(dirname "$0")
ROOTDIR=$BASEDIR/../../..
echo $ROOTDIR
rm -rf $ROOTDIR/dist/icons/achievements
mkdir -p $ROOTDIR/dist/icons
cp -r $BASEDIR/real $ROOTDIR/dist/icons/achievements
for i in $ROOTDIR/dist/icons/achievements/*.svg; do
echo $i
# Make background transparent and replace green with black
# The icons will be recolored by css filters matching the player's theme
# MacOS uses FreeBSD-style sed instead of GNU sed
sed -i '' "s/fill:#000000;/fill-opacity: 0%;/g" "$i"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of forking off a whole new script, use the following syntax:
sed -i"" "s/fill:#000000;/fill-opacity: 0%;/g" "$i"

The lack of space after the -i is the key here. This works for GNU sed (I tested it); it should work for BSD (aka MacOS) sed as well (you can test).

sed -i '' "s/fill:#00ff00;/fill:#000000;/g" "$i"
done
46 changes: 46 additions & 0 deletions src/Achievements/AchievementCategory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";

import { Accordion, AccordionSummary, AccordionDetails, Typography } from "@mui/material";

import { Achievement } from "./Achievements";

interface IProps {
title: string;
achievements: { achievement: Achievement }[];
allAchievements?: { achievement: Achievement }[];
sx?: boolean;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sx prop name doesn't make sense, and also it isn't the right type for a usual sx prop.

I would either call this something like "pad", or just pass a full sx through.

}

function steamCount(achievements: { achievement: Achievement }[]): number {
return achievements.filter((entry) => !entry.achievement.NotInSteam).length;
}

export function AchievementCategory({
title,
achievements,
allAchievements,
sx,
children,
}: React.PropsWithChildren<IProps>): JSX.Element {
// Most parts of the four categories in the old code were very similar (besides the content of
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines can probably be removed. Comments should document the code as it is, not in reference to as it was. It was useful to read right now, though.

// AccordianDetails), with the Acquired category having a few differences,
// although both the Acquired and Locked categories also had an extra prop in the AccordianDetails.
// The 264px minWidth feels scuffed, but fixes an edge case.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please say what the edge case is.

return (
<Accordion defaultExpanded={!!allAchievements} disableGutters square sx={{ minWidth: "264px" }}>
<AccordionSummary>
{allAchievements ? (
<Typography variant="h5" sx={{ my: 1 }}>
{title} ({achievements.length}/{allAchievements.length}, {steamCount(achievements)}/
{steamCount(allAchievements)} for Steam)
</Typography>
) : (
<Typography variant="h5" color="secondary">
{title} ({achievements.length} remaining, {steamCount(achievements)} for Steam)
</Typography>
)}
</AccordionSummary>
<AccordionDetails sx={sx ? { pt: 2 } : undefined}>{children}</AccordionDetails>
</Accordion>
);
}
3 changes: 1 addition & 2 deletions src/Achievements/AchievementData.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"note": "***** Generated from a script, overwritten by steam achievements data *****",
"fetchedOn": 1641517584274,
"note": "Originally generated by a script using Steam achievement data. Going forward, must be edited manually.",
"achievements": {
"CYBERSEC": {
"ID": "CYBERSEC",
Expand Down
19 changes: 18 additions & 1 deletion src/Achievements/AchievementEntry.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";

import { Box, Typography } from "@mui/material";
import LinkOffIcon from "@mui/icons-material/LinkOff";

import { Achievement } from "./Achievements";
import { Settings } from "../Settings/Settings";
Expand All @@ -23,6 +24,7 @@ export function AchievementEntry({
const isUnlocked = !!unlockedOn;

const mainColor = isUnlocked ? Settings.theme.primary : Settings.theme.secondarylight;
const captionColor = isUnlocked ? Settings.theme.primarydark : Settings.theme.secondary;

let achievedOn = "";
if (unlockedOn) {
Expand Down Expand Up @@ -64,10 +66,25 @@ export function AchievementEntry({
{achievement.Description}
</Typography>
{isUnlocked && (
<Typography variant="caption" sx={{ fontSize: "12px", color: Settings.theme.primarydark }}>
<Typography variant="caption" sx={{ fontSize: "12px", color: captionColor }}>
Acquired on {achievedOn}
</Typography>
)}
{achievement.NotInSteam && (
<Box /* This box is used to vertically center the taller LinkOffIcon with the Typography */
sx={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
alignItems: "center",
}}
>
<LinkOffIcon sx={{ fontSize: "20px", color: captionColor, marginRight: 1 }} />
<Typography variant="caption" sx={{ fontSize: "12px", color: captionColor }}>
No equivalent Steam achievement
</Typography>
</Box>
)}
</Box>
</Box>
</Box>
Expand Down
106 changes: 38 additions & 68 deletions src/Achievements/AchievementList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from "react";

import { Accordion, AccordionSummary, AccordionDetails, Box, Typography } from "@mui/material";
import { Box, Typography } from "@mui/material";

import { AchievementCategory } from "./AchievementCategory";
import { AchievementEntry } from "./AchievementEntry";
import { Achievement, PlayerAchievement } from "./Achievements";
import { Settings } from "../Settings/Settings";
Expand Down Expand Up @@ -52,79 +53,48 @@ export function AchievementList({ achievements, playerAchievements }: IProps): J
}}
>
{unlocked.length > 0 && (
<Accordion defaultExpanded disableGutters square>
<AccordionSummary>
<Typography variant="h5" sx={{ my: 1 }}>
Acquired ({unlocked.length}/{data.length})
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ pt: 2 }}>
{unlocked.map((item) => (
<AchievementEntry
key={`unlocked_${item.achievement.ID}`}
achievement={item.achievement}
unlockedOn={item.unlockedOn}
cssFiltersUnlocked={cssPrimary}
cssFiltersLocked={cssSecondary}
/>
))}
</AccordionDetails>
</Accordion>
<AchievementCategory title="Acquired" achievements={unlocked} allAchievements={data} sx={true}>
{unlocked.map((item) => (
<AchievementEntry
key={`unlocked_${item.achievement.ID}`}
achievement={item.achievement}
unlockedOn={item.unlockedOn}
cssFiltersUnlocked={cssPrimary}
cssFiltersLocked={cssSecondary}
/>
))}
</AchievementCategory>
)}

{locked.length > 0 && (
<Accordion disableGutters square>
<AccordionSummary>
<Typography variant="h5" color="secondary">
Locked ({locked.length} remaining)
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ pt: 2 }}>
{locked.map((item) => (
<AchievementEntry
key={`locked_${item.achievement.ID}`}
achievement={item.achievement}
cssFiltersUnlocked={cssPrimary}
cssFiltersLocked={cssSecondary}
/>
))}
</AccordionDetails>
</Accordion>
<AchievementCategory title="Locked" achievements={locked} sx={true}>
{locked.map((item) => (
<AchievementEntry
key={`locked_${item.achievement.ID}`}
achievement={item.achievement}
cssFiltersUnlocked={cssPrimary}
cssFiltersLocked={cssSecondary}
/>
))}
</AchievementCategory>
)}

{unavailable.length > 0 && (
<Accordion disableGutters square>
<AccordionSummary>
<Typography variant="h5" color="secondary">
Unavailable ({unavailable.length} remaining)
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography sx={{ mt: 1 }}>
{unavailable.length} additional achievements hidden behind content you don't have access to.
</Typography>
</AccordionDetails>
</Accordion>
<AchievementCategory title="Unavailable" achievements={unavailable}>
<Typography sx={{ mt: 1 }}>
{unavailable.length} additional achievements hidden behind content you don't have access to.
</Typography>
</AchievementCategory>
)}

{secret.length > 0 && (
<Accordion disableGutters square>
<AccordionSummary>
<Typography variant="h5" color="secondary">
Secret ({secret.length} remaining)
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography color="secondary" sx={{ mt: 1 }}>
{secret.map((item) => (
<span key={`secret_${item.achievement.ID}`}>
<CorruptableText content={item.achievement.ID} spoiler={true}></CorruptableText>
<br />
</span>
))}
</Typography>
</AccordionDetails>
</Accordion>
<AchievementCategory title="Secret" achievements={secret}>
<Typography color="secondary" sx={{ mt: 1 }}>
{secret.map((item) => (
<span key={`secret_${item.achievement.ID}`}>
<CorruptableText content={item.achievement.ID} spoiler={true}></CorruptableText>
<br />
</span>
))}
</Typography>
</AchievementCategory>
)}
</Box>
</Box>
Expand Down
1 change: 1 addition & 0 deletions src/Achievements/Achievements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface Achievement {
Name?: string;
Description?: string;
Secret?: boolean;
NotInSteam?: boolean;
Condition: () => boolean;
Visible?: () => boolean;
AdditionalUnlock?: string[]; // IDs of achievements that should be awarded when awarding this one
Expand Down
16 changes: 10 additions & 6 deletions src/Achievements/AchievementsRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import React from "react";

import { Theme } from "@mui/material/styles";
import { AchievementList } from "./AchievementList";
import { achievements } from "./Achievements";
import { Typography } from "@mui/material";
import { Box, Typography } from "@mui/material";
import { Player } from "@player";
import { makeStyles } from "tss-react/mui";

const useStyles = makeStyles()((theme: Theme) => ({
const useStyles = makeStyles()({
root: {
width: 50,
padding: theme.spacing(2),
userSelect: "none",
},
}));
});

export function AchievementsRoot(): JSX.Element {
const { classes } = useStyles();
return (
<div className={classes.root} style={{ width: "90%" }}>
<Typography variant="h4">Achievements</Typography>
<AchievementList achievements={Object.values(achievements)} playerAchievements={Player.achievements} />
<Box mx={2}>
<Typography>
Achievements are persistent rewards for various actions and challenges. A limited number of Bitburner
achievements have corresponding achievements in Steam.
</Typography>
<AchievementList achievements={Object.values(achievements)} playerAchievements={Player.achievements} />
</Box>
</div>
);
}
12 changes: 7 additions & 5 deletions src/Achievements/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Adding Achievements

- Add a .svg in `./assets/Steam/achievements/real`
- Create the achievement in Steam Dev Portal
- Run `sh ./assets/Steam/achievements/pack-for-web.sh`
- Run `node ./tools/fetch-steam-achievements-data DEVKEYHERE`
- Get your key here: https://steamcommunity.com/dev/apikey
- If making a Steam achievement, create the achievement in Steam Dev Portal
- Run `sh ./assets/Steam/achievements/pack-for-web.sh`, or `pack-for-web-mac.sh` for MacOS
- Add an entry in `./src/Achievements/AchievementData.json` -> achievements
- It should match the information for the Steam achievement, if applicable
- Order the new achievement entry thematically
- Add an entry in `./src/Achievements/Achievements.ts` -> achievements
- Commit `./dist/icons/achievements` & `./src/Achievements/AchievementData.json`
- Match the order of achievements in `AchievementData.json`
- Commit `./dist/icons/achievements`
Loading