Skip to content

Commit 86aa11f

Browse files
committed
feat: more achievements.
1 parent 24da462 commit 86aa11f

File tree

7 files changed

+131
-3
lines changed

7 files changed

+131
-3
lines changed

src/components/Stopwatch.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import React, { useState, useEffect, useRef } from 'react';
2+
import { useAchievements } from '../context/AchievementContext';
23

34
const Stopwatch = () => {
45
const [time, setTime] = useState(0);
56
const [laps, setLaps] = useState([]);
67
const [isRunning, setIsRunning] = useState(false);
78
const intervalRef = useRef(null);
9+
const { unlockAchievement } = useAchievements();
810

911
useEffect(() => {
1012
if (isRunning) {
@@ -23,6 +25,10 @@ const Stopwatch = () => {
2325
};
2426
const handleStop = () => {
2527
setIsRunning(false);
28+
console.log(time)
29+
if (time === 10000) {
30+
unlockAchievement('perfect_timing');
31+
}
2632
};
2733
const handleReset = () => {
2834
setIsRunning(false);

src/config/achievements.js

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,15 @@ import {
2121
KanbanIcon,
2222
PhoneIcon,
2323
ShuffleIcon,
24-
QuestionMarkIcon
24+
QuestionMarkIcon,
25+
TrashIcon,
26+
Metronome as MetronomeIcon,
27+
Spade as SpadeIcon,
28+
Heart as HeartIcon,
29+
Diamond as DiamondIcon,
30+
Eye as EyeIcon,
31+
Target as TargetIcon,
32+
Brain as BrainIcon
2533
} from '@phosphor-icons/react';
2634

2735
export const ACHIEVEMENTS = [
@@ -593,4 +601,74 @@ export const ACHIEVEMENTS = [
593601
icon: <ShuffleIcon size={32} weight="duotone" />,
594602
category: 'Secret',
595603
},
604+
{
605+
id: 'clean_slate',
606+
title: 'Clean Slate',
607+
description: 'Cleared all local storage.',
608+
icon: <TrashIcon size={32} weight="duotone" />,
609+
category: 'Secret',
610+
},
611+
{
612+
id: 'perfect_timing',
613+
title: 'Perfect Timing',
614+
description: 'Stopped the stopwatch at exactly 10.00 seconds.',
615+
icon: <TimerIcon size={32} weight="duotone" />,
616+
category: 'Secret',
617+
},
618+
{
619+
id: 'on_the_beat',
620+
title: 'On the Beat',
621+
description: 'Achieved exactly 90 BPM in the BPM Guesser.',
622+
icon: <MetronomeIcon size={32} weight="duotone" />,
623+
category: 'Secret',
624+
},
625+
{
626+
id: 'card_shark',
627+
title: 'Card Shark',
628+
description: 'Scored over 7 in Higher or Lower.',
629+
icon: <SpadeIcon size={32} weight="duotone" />,
630+
category: 'Secret',
631+
},
632+
{
633+
id: 'high_roller',
634+
title: 'High Roller',
635+
description: 'Scored over 14 in Higher or Lower.',
636+
icon: <HeartIcon size={32} weight="duotone" />,
637+
category: 'Secret',
638+
},
639+
{
640+
id: 'legendary_gambler',
641+
title: 'Legendary Gambler',
642+
description: 'Scored over 20 in Higher or Lower.',
643+
icon: <DiamondIcon size={32} weight="duotone" />,
644+
category: 'Secret',
645+
},
646+
{
647+
id: 'sharp_eye',
648+
title: 'Sharp Eye',
649+
description: 'Completed Memory Game in 24 moves or less.',
650+
icon: <EyeIcon size={32} weight="duotone" />,
651+
category: 'Secret',
652+
},
653+
{
654+
id: 'eidetic_memory',
655+
title: 'Eidetic Memory',
656+
description: 'Completed Memory Game in 18 moves or less.',
657+
icon: <TargetIcon size={32} weight="duotone" />,
658+
category: 'Secret',
659+
},
660+
{
661+
id: 'mind_palace',
662+
title: 'Mind Palace',
663+
description: 'Completed Memory Game in 14 moves or less.',
664+
icon: <BrainIcon size={32} weight="duotone" />,
665+
category: 'Secret',
666+
},
667+
{
668+
id: 'ctu_agent',
669+
title: 'CTU Agent',
670+
description: 'The clock is ticking...',
671+
icon: <PhoneIcon size={32} weight="duotone" />,
672+
category: 'Secret',
673+
},
596674
];

src/pages/SettingsPage.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,25 @@ const SettingsPage = () => {
122122
message: 'All local storage data has been cleared. The page will now reload.',
123123
duration: 3000,
124124
});
125+
126+
setTimeout(() => {
127+
// Manually set the achievement to avoid restoring old state via context
128+
const now = new Date().toISOString();
129+
const cleanSlateData = {
130+
clean_slate: { unlocked: true, unlockedAt: now }
131+
};
132+
// We use the raw key 'unlocked-achievements' as defined in AchievementContext
133+
localStorage.setItem('unlocked-achievements', JSON.stringify(cleanSlateData));
134+
135+
addToast({
136+
title: 'Achievement Unlocked!',
137+
message: 'Clean Slate',
138+
duration: 4000,
139+
icon: <Trophy size={24} weight="duotone" />,
140+
type: 'gold',
141+
});
142+
}, 500);
143+
125144
setTimeout(() => {
126145
window.location.reload();
127146
}, 3000);

src/pages/apps/BpmGuesserPage.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import React, { useState, useRef } from 'react';
1+
import React, { useState, useRef, useEffect } from 'react';
22
import { Link } from 'react-router-dom';
33
import { ArrowLeftIcon, MetronomeIcon } from '@phosphor-icons/react';
44
import colors from '../../config/colors';
55
import useSeo from '../../hooks/useSeo';
66
import BreadcrumbTitle from '../../components/BreadcrumbTitle';
7+
import { useAchievements } from '../../context/AchievementContext';
78

89
const BpmGuesserPage = () => {
910
useSeo({
@@ -30,6 +31,13 @@ const BpmGuesserPage = () => {
3031
const [bpm, setBpm] = useState(0);
3132
const [taps, setTaps] = useState([]);
3233
const lastTapTime = useRef(0);
34+
const { unlockAchievement } = useAchievements();
35+
36+
useEffect(() => {
37+
if (bpm === 90) {
38+
unlockAchievement('on_the_beat');
39+
}
40+
}, [bpm, unlockAchievement]);
3341

3442
const handleTap = () => {
3543
const now = performance.now();

src/pages/apps/CardGamePage.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useToast } from '../../hooks/useToast';
66
import useSeo from '../../hooks/useSeo';
77
import '../../styles/CardGamePage.css';
88
import BreadcrumbTitle from '../../components/BreadcrumbTitle';
9+
import { useAchievements } from '../../context/AchievementContext';
910

1011
const suits = ['♠', '♥', '♦', '♣'];
1112
const ranks = [
@@ -66,6 +67,7 @@ const CardGamePage = () => {
6667
});
6768

6869
const { addToast } = useToast();
70+
const { unlockAchievement } = useAchievements();
6971
const [deck, setDeck] = useState([]);
7072
const [currentCard, setCurrentCard] = useState(null);
7173
const [nextCard, setNextCard] = useState(null);
@@ -130,6 +132,15 @@ const CardGamePage = () => {
130132
setNextCard(null);
131133
} else {
132134
setGameOver(true);
135+
if (score > 20) {
136+
unlockAchievement('legendary_gambler');
137+
}
138+
if (score > 14) {
139+
unlockAchievement('high_roller');
140+
}
141+
if (score > 7) {
142+
unlockAchievement('card_shark');
143+
}
133144
addToast({
134145
title: 'Game Over!',
135146
message: `It was a ${drawnNextCard.rank} of ${drawnNextCard.suit}. Final score: ${score}`,

src/pages/apps/MemoryGamePage.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import colors from '../../config/colors';
55
import useSeo from '../../hooks/useSeo';
66
import '../../styles/MemoryGamePage.css';
77
import BreadcrumbTitle from '../../components/BreadcrumbTitle';
8+
import { useAchievements } from '../../context/AchievementContext';
89

910
const cardValues = ['🍎', '🍌', '🍒', '🍇', '🍋', '🍊', '🍓', '🍉']; // Example card values
1011

@@ -28,6 +29,7 @@ const MemoryGamePage = () => {
2829
twitterImage: 'https://fezcode.github.io/logo512.png',
2930
});
3031

32+
const { unlockAchievement } = useAchievements();
3133
const [cards, setCards] = useState([]);
3234
const [flippedCards, setFlippedCards] = useState([]);
3335
const [matchesFound, setMatchesFound] = useState(0);
@@ -118,8 +120,11 @@ const MemoryGamePage = () => {
118120
useEffect(() => {
119121
if (matchesFound === cardValues.length) {
120122
setGameOver(true);
123+
if (moves <= 24) unlockAchievement('sharp_eye');
124+
if (moves <= 18) unlockAchievement('eidetic_memory');
125+
if (moves <= 14) unlockAchievement('mind_palace');
121126
}
122-
}, [matchesFound]);
127+
}, [matchesFound, moves, unlockAchievement]);
123128

124129
const cardStyle = {
125130
backgroundColor: colors['app-alpha-10'],

src/pages/apps/RotaryPhonePage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ const RotaryPhonePage = () => {
8888
'20': 'critical_hit',
8989
'21': 'savage',
9090
'80': 'www',
91+
'24': 'ctu_agent',
9192
}
9293

9394
const handleCall = () => {

0 commit comments

Comments
 (0)