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 |
|