update
Browse files- .DS_Store +0 -0
- client/index.html +10 -2
- client/src/components/StoryChoices.jsx +5 -5
- client/src/index.css +0 -1
- client/src/pages/Game.jsx +134 -99
- client/src/pages/Home.jsx +69 -9
- client/src/pages/Tutorial.jsx +161 -58
.DS_Store
CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
|
|
client/index.html
CHANGED
@@ -1,10 +1,18 @@
|
|
1 |
-
<!
|
2 |
<html lang="en">
|
3 |
<head>
|
4 |
<meta charset="UTF-8" />
|
5 |
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
-
<title>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
</head>
|
9 |
<body>
|
10 |
<div id="root"></div>
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
<html lang="en">
|
3 |
<head>
|
4 |
<meta charset="UTF-8" />
|
5 |
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
+
<title>Sarah's Chronicles</title>
|
8 |
+
<style>
|
9 |
+
html,
|
10 |
+
body {
|
11 |
+
margin: 0;
|
12 |
+
padding: 0;
|
13 |
+
background-color: #121212; /* MUI dark theme background color */
|
14 |
+
}
|
15 |
+
</style>
|
16 |
</head>
|
17 |
<body>
|
18 |
<div id="root"></div>
|
client/src/components/StoryChoices.jsx
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
import { Box, Button, Typography, Chip } from "@mui/material";
|
2 |
-
import { useStoryCapture } from "../hooks/useStoryCapture";
|
3 |
|
4 |
// Function to convert text with ** to Chip elements
|
5 |
const formatTextWithBold = (text) => {
|
@@ -33,8 +32,6 @@ export function StoryChoices({
|
|
33 |
isGameOver = false,
|
34 |
containerRef,
|
35 |
}) {
|
36 |
-
const { captureStory } = useStoryCapture();
|
37 |
-
|
38 |
console.log("ICI", isLastStep, isGameOver);
|
39 |
if (isGameOver) {
|
40 |
return (
|
@@ -65,7 +62,10 @@ export function StoryChoices({
|
|
65 |
<Button
|
66 |
variant="outlined"
|
67 |
size="large"
|
68 |
-
onClick={() =>
|
|
|
|
|
|
|
69 |
sx={{
|
70 |
width: "100%",
|
71 |
textTransform: "none",
|
@@ -137,7 +137,7 @@ export function StoryChoices({
|
|
137 |
}}
|
138 |
>
|
139 |
<Typography variant="caption" sx={{ opacity: 0.7, color: "white" }}>
|
140 |
-
|
141 |
</Typography>
|
142 |
<Button
|
143 |
variant="outlined"
|
|
|
1 |
import { Box, Button, Typography, Chip } from "@mui/material";
|
|
|
2 |
|
3 |
// Function to convert text with ** to Chip elements
|
4 |
const formatTextWithBold = (text) => {
|
|
|
32 |
isGameOver = false,
|
33 |
containerRef,
|
34 |
}) {
|
|
|
|
|
35 |
console.log("ICI", isLastStep, isGameOver);
|
36 |
if (isGameOver) {
|
37 |
return (
|
|
|
62 |
<Button
|
63 |
variant="outlined"
|
64 |
size="large"
|
65 |
+
onClick={() => {
|
66 |
+
// Simulate a button click on another button with the id "targetButton"
|
67 |
+
document.getElementById("printButton").click();
|
68 |
+
}}
|
69 |
sx={{
|
70 |
width: "100%",
|
71 |
textTransform: "none",
|
|
|
137 |
}}
|
138 |
>
|
139 |
<Typography variant="caption" sx={{ opacity: 0.7, color: "white" }}>
|
140 |
+
Choice {index + 1}
|
141 |
</Typography>
|
142 |
<Button
|
143 |
variant="outlined"
|
client/src/index.css
CHANGED
@@ -25,7 +25,6 @@ html,
|
|
25 |
body,
|
26 |
#root {
|
27 |
min-height: 100vh;
|
28 |
-
background-color: #f5f5f5;
|
29 |
overflow: hidden;
|
30 |
font-weight: bold;
|
31 |
}
|
|
|
25 |
body,
|
26 |
#root {
|
27 |
min-height: 100vh;
|
|
|
28 |
overflow: hidden;
|
29 |
font-weight: bold;
|
30 |
}
|
client/src/pages/Game.jsx
CHANGED
@@ -1,5 +1,7 @@
|
|
1 |
import { useState, useEffect, useRef } from "react";
|
2 |
import { Box, LinearProgress, IconButton, Tooltip } from "@mui/material";
|
|
|
|
|
3 |
import { ComicLayout } from "../layouts/ComicLayout";
|
4 |
import { storyApi } from "../utils/api";
|
5 |
import { useNarrator } from "../hooks/useNarrator";
|
@@ -10,7 +12,8 @@ import { StoryChoices } from "../components/StoryChoices";
|
|
10 |
import { ErrorDisplay } from "../components/ErrorDisplay";
|
11 |
import VolumeUpIcon from "@mui/icons-material/VolumeUp";
|
12 |
import VolumeOffIcon from "@mui/icons-material/VolumeOff";
|
13 |
-
import
|
|
|
14 |
import { getNextLayoutType, LAYOUTS } from "../layouts/config";
|
15 |
|
16 |
// Constants
|
@@ -34,6 +37,7 @@ const stripBoldMarkers = (text) => {
|
|
34 |
};
|
35 |
|
36 |
export function Game() {
|
|
|
37 |
const storyContainerRef = useRef(null);
|
38 |
const { downloadStoryImage } = useStoryCapture();
|
39 |
const [storySegments, setStorySegments] = useState([]);
|
@@ -61,6 +65,11 @@ export function Game() {
|
|
61 |
handleStoryAction("restart");
|
62 |
}, []);
|
63 |
|
|
|
|
|
|
|
|
|
|
|
64 |
const handleChoice = async (choiceId) => {
|
65 |
playPageSound();
|
66 |
|
@@ -275,114 +284,140 @@ export function Game() {
|
|
275 |
};
|
276 |
|
277 |
return (
|
278 |
-
<
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
position: "relative",
|
285 |
-
overflow: "hidden",
|
286 |
-
}}
|
287 |
>
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
}
|
307 |
-
handleStoryAction(
|
308 |
-
"choice",
|
309 |
-
storySegments[storySegments.length - 1]?.choiceId || null
|
310 |
-
);
|
311 |
-
}
|
312 |
-
}}
|
313 |
-
/>
|
314 |
-
) : (
|
315 |
-
<>
|
316 |
-
<ComicLayout
|
317 |
-
segments={storySegments}
|
318 |
-
choices={showChoices ? currentChoices : []}
|
319 |
-
onChoice={handleChoice}
|
320 |
-
isLoading={isLoading}
|
321 |
-
showScreenshot={storySegments.length > 0}
|
322 |
-
onScreenshot={handleCaptureStory}
|
323 |
/>
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
isLastStep={
|
330 |
-
storySegments.length > 0 &&
|
331 |
-
storySegments[storySegments.length - 1].isLastStep
|
332 |
-
}
|
333 |
-
isGameOver={
|
334 |
-
storySegments.length > 0 &&
|
335 |
-
storySegments[storySegments.length - 1].isGameOver
|
336 |
-
}
|
337 |
-
containerRef={storyContainerRef}
|
338 |
-
/>
|
339 |
-
)}
|
340 |
-
<Box
|
341 |
sx={{
|
342 |
-
position: "
|
343 |
top: 16,
|
344 |
-
|
345 |
-
|
346 |
-
gap: 1,
|
347 |
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
348 |
-
|
349 |
-
|
|
|
|
|
350 |
}}
|
351 |
>
|
352 |
-
<
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
isNarrationEnabled ? "Disable narration" : "Enable narration"
|
368 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
369 |
>
|
370 |
-
<
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
378 |
>
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
386 |
);
|
387 |
}
|
388 |
|
|
|
1 |
import { useState, useEffect, useRef } from "react";
|
2 |
import { Box, LinearProgress, IconButton, Tooltip } from "@mui/material";
|
3 |
+
import { useNavigate } from "react-router-dom";
|
4 |
+
import { motion } from "framer-motion";
|
5 |
import { ComicLayout } from "../layouts/ComicLayout";
|
6 |
import { storyApi } from "../utils/api";
|
7 |
import { useNarrator } from "../hooks/useNarrator";
|
|
|
12 |
import { ErrorDisplay } from "../components/ErrorDisplay";
|
13 |
import VolumeUpIcon from "@mui/icons-material/VolumeUp";
|
14 |
import VolumeOffIcon from "@mui/icons-material/VolumeOff";
|
15 |
+
import PhotoCameraOutlinedIcon from "@mui/icons-material/PhotoCameraOutlined";
|
16 |
+
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
17 |
import { getNextLayoutType, LAYOUTS } from "../layouts/config";
|
18 |
|
19 |
// Constants
|
|
|
37 |
};
|
38 |
|
39 |
export function Game() {
|
40 |
+
const navigate = useNavigate();
|
41 |
const storyContainerRef = useRef(null);
|
42 |
const { downloadStoryImage } = useStoryCapture();
|
43 |
const [storySegments, setStorySegments] = useState([]);
|
|
|
65 |
handleStoryAction("restart");
|
66 |
}, []);
|
67 |
|
68 |
+
const handleBack = () => {
|
69 |
+
playPageSound();
|
70 |
+
navigate("/tutorial");
|
71 |
+
};
|
72 |
+
|
73 |
const handleChoice = async (choiceId) => {
|
74 |
playPageSound();
|
75 |
|
|
|
284 |
};
|
285 |
|
286 |
return (
|
287 |
+
<motion.div
|
288 |
+
initial={{ opacity: 0 }}
|
289 |
+
animate={{ opacity: 1 }}
|
290 |
+
exit={{ opacity: 0 }}
|
291 |
+
transition={{ duration: 0.3, ease: "easeInOut" }}
|
292 |
+
style={{ backgroundColor: "#121212", width: "100%" }}
|
|
|
|
|
|
|
293 |
>
|
294 |
+
<Box
|
295 |
+
ref={storyContainerRef}
|
296 |
+
sx={{
|
297 |
+
height: "100vh",
|
298 |
+
width: "100vw",
|
299 |
+
backgroundColor: "#1a1a1a",
|
300 |
+
position: "relative",
|
301 |
+
overflow: "hidden",
|
302 |
+
}}
|
303 |
+
>
|
304 |
+
{isLoading && (
|
305 |
+
<LinearProgress
|
306 |
+
sx={{
|
307 |
+
position: "absolute",
|
308 |
+
top: 0,
|
309 |
+
left: 0,
|
310 |
+
right: 0,
|
311 |
+
zIndex: 1000,
|
312 |
+
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
313 |
/>
|
314 |
+
)}
|
315 |
+
|
316 |
+
<Tooltip title="Back to tutorial">
|
317 |
+
<IconButton
|
318 |
+
onClick={handleBack}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
319 |
sx={{
|
320 |
+
position: "absolute",
|
321 |
top: 16,
|
322 |
+
left: 16,
|
323 |
+
color: "white",
|
|
|
324 |
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
325 |
+
"&:hover": {
|
326 |
+
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
327 |
+
},
|
328 |
+
zIndex: 1000,
|
329 |
}}
|
330 |
>
|
331 |
+
<ArrowBackIcon />
|
332 |
+
</IconButton>
|
333 |
+
</Tooltip>
|
334 |
+
|
335 |
+
{error ? (
|
336 |
+
<ErrorDisplay
|
337 |
+
message={error}
|
338 |
+
onRetry={() => {
|
339 |
+
if (storySegments.length === 0) {
|
340 |
+
handleStoryAction("restart");
|
341 |
+
} else {
|
342 |
+
handleStoryAction(
|
343 |
+
"choice",
|
344 |
+
storySegments[storySegments.length - 1]?.choiceId || null
|
345 |
+
);
|
|
|
346 |
}
|
347 |
+
}}
|
348 |
+
/>
|
349 |
+
) : (
|
350 |
+
<>
|
351 |
+
<ComicLayout
|
352 |
+
segments={storySegments}
|
353 |
+
choices={showChoices ? currentChoices : []}
|
354 |
+
onChoice={handleChoice}
|
355 |
+
isLoading={isLoading}
|
356 |
+
showScreenshot={storySegments.length > 0}
|
357 |
+
onScreenshot={handleCaptureStory}
|
358 |
+
/>
|
359 |
+
{showChoices && (
|
360 |
+
<StoryChoices
|
361 |
+
choices={currentChoices}
|
362 |
+
onChoice={handleChoice}
|
363 |
+
disabled={isLoading}
|
364 |
+
isLastStep={
|
365 |
+
storySegments.length > 0 &&
|
366 |
+
storySegments[storySegments.length - 1].isLastStep
|
367 |
+
}
|
368 |
+
isGameOver={
|
369 |
+
storySegments.length > 0 &&
|
370 |
+
storySegments[storySegments.length - 1].isGameOver
|
371 |
+
}
|
372 |
+
containerRef={storyContainerRef}
|
373 |
+
/>
|
374 |
+
)}
|
375 |
+
<Box
|
376 |
+
sx={{
|
377 |
+
position: "fixed",
|
378 |
+
top: 16,
|
379 |
+
right: 16,
|
380 |
+
display: "flex",
|
381 |
+
gap: 1,
|
382 |
+
padding: 1,
|
383 |
+
borderRadius: 1,
|
384 |
+
}}
|
385 |
>
|
386 |
+
<Tooltip title="Save your story">
|
387 |
+
<IconButton
|
388 |
+
onClick={handleCaptureStory}
|
389 |
+
sx={{
|
390 |
+
color: "white",
|
391 |
+
"&:hover": {
|
392 |
+
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
393 |
+
},
|
394 |
+
}}
|
395 |
+
>
|
396 |
+
<PhotoCameraOutlinedIcon />
|
397 |
+
</IconButton>
|
398 |
+
</Tooltip>
|
399 |
+
<Tooltip
|
400 |
+
title={
|
401 |
+
isNarrationEnabled ? "Disable narration" : "Enable narration"
|
402 |
+
}
|
403 |
>
|
404 |
+
<IconButton
|
405 |
+
onClick={() => setIsNarrationEnabled(!isNarrationEnabled)}
|
406 |
+
sx={{
|
407 |
+
color: "white",
|
408 |
+
"&:hover": {
|
409 |
+
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
410 |
+
},
|
411 |
+
}}
|
412 |
+
>
|
413 |
+
{isNarrationEnabled ? <VolumeUpIcon /> : <VolumeOffIcon />}
|
414 |
+
</IconButton>
|
415 |
+
</Tooltip>
|
416 |
+
</Box>
|
417 |
+
</>
|
418 |
+
)}
|
419 |
+
</Box>
|
420 |
+
</motion.div>
|
421 |
);
|
422 |
}
|
423 |
|
client/src/pages/Home.jsx
CHANGED
@@ -1,16 +1,24 @@
|
|
1 |
-
import { Box, Button } from "@mui/material";
|
2 |
import { motion } from "framer-motion";
|
3 |
import { useNavigate } from "react-router-dom";
|
|
|
4 |
|
5 |
export function Home() {
|
6 |
const navigate = useNavigate();
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
return (
|
9 |
<motion.div
|
10 |
initial={{ opacity: 0 }}
|
11 |
animate={{ opacity: 1 }}
|
12 |
exit={{ opacity: 0 }}
|
13 |
-
transition={{ duration: 0.
|
|
|
14 |
>
|
15 |
<Box
|
16 |
sx={{
|
@@ -24,20 +32,72 @@ export function Home() {
|
|
24 |
}}
|
25 |
>
|
26 |
<Box
|
27 |
-
component="img"
|
28 |
-
src="/book.webp"
|
29 |
-
alt="Book cover"
|
30 |
sx={{
|
|
|
31 |
height: "80vh",
|
32 |
width: "auto",
|
33 |
-
objectFit: "contain",
|
34 |
-
borderRadius: "12px",
|
35 |
}}
|
36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
<Button
|
38 |
variant="outlined"
|
39 |
size="large"
|
40 |
-
onClick={
|
41 |
sx={{
|
42 |
fontSize: "1.2rem",
|
43 |
padding: "12px 36px",
|
|
|
1 |
+
import { Box, Button, Typography } from "@mui/material";
|
2 |
import { motion } from "framer-motion";
|
3 |
import { useNavigate } from "react-router-dom";
|
4 |
+
import { usePageSound } from "../hooks/usePageSound";
|
5 |
|
6 |
export function Home() {
|
7 |
const navigate = useNavigate();
|
8 |
+
const playPageSound = usePageSound();
|
9 |
+
|
10 |
+
const handlePlay = () => {
|
11 |
+
playPageSound();
|
12 |
+
navigate("/tutorial");
|
13 |
+
};
|
14 |
|
15 |
return (
|
16 |
<motion.div
|
17 |
initial={{ opacity: 0 }}
|
18 |
animate={{ opacity: 1 }}
|
19 |
exit={{ opacity: 0 }}
|
20 |
+
transition={{ duration: 0.3, ease: "easeInOut" }}
|
21 |
+
style={{ backgroundColor: "#121212", width: "100%" }}
|
22 |
>
|
23 |
<Box
|
24 |
sx={{
|
|
|
32 |
}}
|
33 |
>
|
34 |
<Box
|
|
|
|
|
|
|
35 |
sx={{
|
36 |
+
position: "relative",
|
37 |
height: "80vh",
|
38 |
width: "auto",
|
|
|
|
|
39 |
}}
|
40 |
+
>
|
41 |
+
<Box
|
42 |
+
component="img"
|
43 |
+
src="/book.webp"
|
44 |
+
alt="Book cover"
|
45 |
+
sx={{
|
46 |
+
height: "100%",
|
47 |
+
width: "auto",
|
48 |
+
objectFit: "contain",
|
49 |
+
borderRadius: "4px",
|
50 |
+
}}
|
51 |
+
/>
|
52 |
+
<Box
|
53 |
+
sx={{
|
54 |
+
position: "absolute",
|
55 |
+
top: "75%",
|
56 |
+
left: "50%",
|
57 |
+
transform: "translate(-50%, -50%)",
|
58 |
+
textAlign: "center",
|
59 |
+
color: "white",
|
60 |
+
textShadow: "2px 2px 4px rgba(0,0,0,0.15)",
|
61 |
+
}}
|
62 |
+
>
|
63 |
+
<Typography
|
64 |
+
variant="h2"
|
65 |
+
component="h1"
|
66 |
+
sx={{
|
67 |
+
fontWeight: "bold",
|
68 |
+
marginBottom: 2,
|
69 |
+
}}
|
70 |
+
>
|
71 |
+
Sarah's Chronicles
|
72 |
+
</Typography>
|
73 |
+
</Box>
|
74 |
+
<Box
|
75 |
+
sx={{
|
76 |
+
position: "absolute",
|
77 |
+
bottom: 32,
|
78 |
+
left: "50%",
|
79 |
+
transform: "translateX(-50%)",
|
80 |
+
textAlign: "center",
|
81 |
+
color: "white",
|
82 |
+
textShadow: "2px 2px 4px rgba(0,0,0,0.15)",
|
83 |
+
}}
|
84 |
+
>
|
85 |
+
<Typography
|
86 |
+
variant="caption"
|
87 |
+
display="block"
|
88 |
+
sx={{ opacity: 0.9, mb: -1, fontWeight: "black" }}
|
89 |
+
>
|
90 |
+
a story by
|
91 |
+
</Typography>
|
92 |
+
<Typography variant="h6" sx={{ fontWeight: "black" }}>
|
93 |
+
Mistral Small
|
94 |
+
</Typography>
|
95 |
+
</Box>
|
96 |
+
</Box>
|
97 |
<Button
|
98 |
variant="outlined"
|
99 |
size="large"
|
100 |
+
onClick={handlePlay}
|
101 |
sx={{
|
102 |
fontSize: "1.2rem",
|
103 |
padding: "12px 36px",
|
client/src/pages/Tutorial.jsx
CHANGED
@@ -1,72 +1,175 @@
|
|
1 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
import { useNavigate } from "react-router-dom";
|
3 |
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
|
|
|
|
|
|
4 |
|
5 |
export function Tutorial() {
|
6 |
const navigate = useNavigate();
|
|
|
7 |
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
width: "100%",
|
13 |
-
display: "flex",
|
14 |
-
flexDirection: "column",
|
15 |
-
alignItems: "center",
|
16 |
-
justifyContent: "center",
|
17 |
-
gap: 4,
|
18 |
-
padding: 4,
|
19 |
-
backgroundColor: "background.default",
|
20 |
-
color: "text.primary",
|
21 |
-
}}
|
22 |
-
>
|
23 |
-
<Typography variant="h2" component="h1" textAlign="center" gutterBottom>
|
24 |
-
How to play?
|
25 |
-
</Typography>
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
<Chip label="wastelands" size="small" /> in search of food.
|
32 |
-
</Typography>
|
33 |
-
<Typography variant="h5" paragraph>
|
34 |
-
As her little sister, you stayed in the bunker and you help her make
|
35 |
-
decisions with a talkie-walkie.
|
36 |
-
</Typography>
|
37 |
-
-
|
38 |
-
<Typography variant="body1" paragraph>
|
39 |
-
When <Chip label="Sarah" size="small" /> calls you, you have two ways
|
40 |
-
to help her:
|
41 |
-
</Typography>
|
42 |
-
<Typography variant="body1" paragraph sx={{ mb: 4 }}>
|
43 |
-
1. Choose one of the suggested responses by clicking on it
|
44 |
-
<br />
|
45 |
-
2. Use your voice to speak directly to{" "}
|
46 |
-
<Chip label="Sarah" size="small" /> by clicking the "Try to convince{" "}
|
47 |
-
<Chip label="Sarah" size="small" />" button
|
48 |
-
</Typography>
|
49 |
-
-
|
50 |
-
<Typography variant="body1" paragraph>
|
51 |
-
As her sibling and a scientist, you need to find the right words and
|
52 |
-
tone to help her understand the urgency of climate change, while
|
53 |
-
maintaining your close relationship.
|
54 |
-
</Typography>
|
55 |
-
</Box>
|
56 |
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
|
|
|
|
|
|
|
|
|
|
61 |
sx={{
|
62 |
-
|
63 |
-
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
}}
|
66 |
>
|
67 |
-
|
68 |
-
|
69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
);
|
71 |
}
|
72 |
|
|
|
1 |
+
import {
|
2 |
+
Box,
|
3 |
+
Typography,
|
4 |
+
Button,
|
5 |
+
Chip,
|
6 |
+
Paper,
|
7 |
+
IconButton,
|
8 |
+
Tooltip,
|
9 |
+
} from "@mui/material";
|
10 |
import { useNavigate } from "react-router-dom";
|
11 |
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
12 |
+
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
13 |
+
import { usePageSound } from "../hooks/usePageSound";
|
14 |
+
import { motion } from "framer-motion";
|
15 |
|
16 |
export function Tutorial() {
|
17 |
const navigate = useNavigate();
|
18 |
+
const playPageSound = usePageSound();
|
19 |
|
20 |
+
const handleStartGame = () => {
|
21 |
+
playPageSound();
|
22 |
+
navigate("/game");
|
23 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
+
const handleBack = () => {
|
26 |
+
playPageSound();
|
27 |
+
navigate("/");
|
28 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
+
return (
|
31 |
+
<motion.div
|
32 |
+
initial={{ opacity: 0 }}
|
33 |
+
animate={{ opacity: 1 }}
|
34 |
+
exit={{ opacity: 0 }}
|
35 |
+
transition={{ duration: 0.3, ease: "easeInOut" }}
|
36 |
+
style={{ backgroundColor: "#121212", width: "100%" }}
|
37 |
+
>
|
38 |
+
<Box
|
39 |
sx={{
|
40 |
+
minHeight: "100vh",
|
41 |
+
width: "100%",
|
42 |
+
display: "flex",
|
43 |
+
flexDirection: "column",
|
44 |
+
alignItems: "center",
|
45 |
+
justifyContent: "center",
|
46 |
+
gap: 4,
|
47 |
+
padding: 4,
|
48 |
+
backgroundColor: "background.default",
|
49 |
+
position: "relative",
|
50 |
}}
|
51 |
>
|
52 |
+
<Tooltip title="Back to home">
|
53 |
+
<IconButton
|
54 |
+
onClick={handleBack}
|
55 |
+
sx={{
|
56 |
+
position: "absolute",
|
57 |
+
top: 16,
|
58 |
+
left: 16,
|
59 |
+
color: "white",
|
60 |
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
61 |
+
"&:hover": {
|
62 |
+
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
63 |
+
},
|
64 |
+
}}
|
65 |
+
>
|
66 |
+
<ArrowBackIcon />
|
67 |
+
</IconButton>
|
68 |
+
</Tooltip>
|
69 |
+
|
70 |
+
<Box
|
71 |
+
sx={{
|
72 |
+
position: "relative",
|
73 |
+
width: "auto",
|
74 |
+
height: "80vh",
|
75 |
+
aspectRatio: "0.66666667",
|
76 |
+
display: "flex",
|
77 |
+
alignItems: "center", // Center vertically
|
78 |
+
justifyContent: "center", // Center horizontally
|
79 |
+
"&::before, &::after": {
|
80 |
+
content: '""',
|
81 |
+
position: "absolute",
|
82 |
+
width: "100%",
|
83 |
+
height: "100%",
|
84 |
+
background: "white",
|
85 |
+
borderRadius: 1,
|
86 |
+
boxShadow: "0px 1px 3px rgba(0,0,0,0.2)",
|
87 |
+
},
|
88 |
+
"&::before": {
|
89 |
+
top: "4px",
|
90 |
+
left: "4px",
|
91 |
+
transform: "rotate(-1deg)",
|
92 |
+
zIndex: 1,
|
93 |
+
},
|
94 |
+
"&::after": {
|
95 |
+
top: "8px",
|
96 |
+
left: "8px",
|
97 |
+
transform: "rotate(1deg)",
|
98 |
+
zIndex: 0,
|
99 |
+
},
|
100 |
+
}}
|
101 |
+
>
|
102 |
+
<Paper
|
103 |
+
elevation={3}
|
104 |
+
sx={{
|
105 |
+
position: "relative",
|
106 |
+
zIndex: 2,
|
107 |
+
width: "100%",
|
108 |
+
height: "100%",
|
109 |
+
backgroundColor: "white",
|
110 |
+
color: "black",
|
111 |
+
padding: 6,
|
112 |
+
borderRadius: 1,
|
113 |
+
display: "flex",
|
114 |
+
flexDirection: "column",
|
115 |
+
alignItems: "center", // Center align items
|
116 |
+
justifyContent: "center", // Ensure content is centered vertically
|
117 |
+
textAlign: "center",
|
118 |
+
gap: 4,
|
119 |
+
overflowY: "auto",
|
120 |
+
}}
|
121 |
+
>
|
122 |
+
<Typography
|
123 |
+
variant="h2"
|
124 |
+
component="h1"
|
125 |
+
textAlign="center"
|
126 |
+
gutterBottom
|
127 |
+
color="black"
|
128 |
+
sx={{ width: "100%" }}
|
129 |
+
>
|
130 |
+
Synopsis
|
131 |
+
</Typography>
|
132 |
+
<Typography
|
133 |
+
variant="body1"
|
134 |
+
paragraph
|
135 |
+
color="black"
|
136 |
+
sx={{ fontWeight: "normal" }}
|
137 |
+
>
|
138 |
+
Since the rise of <strong>AI</strong>, the world is desolate due
|
139 |
+
to a <strong>nuclear winter</strong> caused by rogue{" "}
|
140 |
+
<strong>AIs</strong> that launched <strong>bombs</strong> all over
|
141 |
+
the planet. You are the only <strong>survivor</strong> of the{" "}
|
142 |
+
<strong>bunker</strong>.
|
143 |
+
<br />
|
144 |
+
<br />
|
145 |
+
You have to make <strong>decisions</strong> to{" "}
|
146 |
+
<strong>survive</strong>. You have ventured out of your{" "}
|
147 |
+
<strong>bunker</strong> to find <strong>medicine</strong> for your{" "}
|
148 |
+
<strong>sick sister</strong>. If you don't find it, she will{" "}
|
149 |
+
<strong>die</strong>. <strong>Time</strong> is running out, and
|
150 |
+
every <strong>choice</strong>
|
151 |
+
matters in this desperate <strong>quest</strong>.
|
152 |
+
</Typography>
|
153 |
+
<Typography variant="h4">How to play</Typography>
|
154 |
+
<Typography variant="body1" sx={{ fontWeight: "normal" }}>
|
155 |
+
At each step, click one of the available <strong>choices</strong>.
|
156 |
+
</Typography>
|
157 |
+
</Paper>
|
158 |
+
</Box>
|
159 |
+
|
160 |
+
<Button
|
161 |
+
variant="outlined"
|
162 |
+
size="large"
|
163 |
+
onClick={handleStartGame}
|
164 |
+
sx={{
|
165 |
+
fontSize: "1.2rem",
|
166 |
+
padding: "12px 24px",
|
167 |
+
}}
|
168 |
+
>
|
169 |
+
Start the game
|
170 |
+
</Button>
|
171 |
+
</Box>
|
172 |
+
</motion.div>
|
173 |
);
|
174 |
}
|
175 |
|