tfrere HF Staff commited on
Commit
f1b4bef
·
1 Parent(s): eec194a
client/src/components/StoryChoices.jsx CHANGED
@@ -129,7 +129,7 @@ export function StoryChoices() {
129
  />
130
  ) : (
131
  <>
132
- {choices
133
  .filter((_, index) => !isMobile || index === 0)
134
  .map((choice, index) => (
135
  <Box
@@ -161,7 +161,36 @@ export function StoryChoices() {
161
  {formatTextWithBold(choice.text)}
162
  </Button>
163
  </Box>
164
- ))}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
  <Box
167
  sx={{
 
129
  />
130
  ) : (
131
  <>
132
+ {/* {choices
133
  .filter((_, index) => !isMobile || index === 0)
134
  .map((choice, index) => (
135
  <Box
 
161
  {formatTextWithBold(choice.text)}
162
  </Button>
163
  </Box>
164
+ ))} */}
165
+
166
+ <Box
167
+ sx={{
168
+ display: "flex",
169
+ flexDirection: "column",
170
+ alignItems: "center",
171
+ gap: 1,
172
+ minWidth: "fit-content",
173
+ maxWidth: isMobile ? "90%" : "30%",
174
+ }}
175
+ >
176
+ <Button
177
+ variant="contained"
178
+ size="large"
179
+ onClick={() => {
180
+ initAudioContext();
181
+ playPageSound();
182
+ stopNarration();
183
+ onChoice(choices[0].id);
184
+ }}
185
+ disabled={isSarahActive || isLoading || isNarratorSpeaking}
186
+ sx={{
187
+ width: "auto",
188
+ minWidth: "fit-content",
189
+ }}
190
+ >
191
+ {formatTextWithBold(choices[0].text)}
192
+ </Button>
193
+ </Box>
194
 
195
  <Box
196
  sx={{
client/src/components/StyledText.jsx CHANGED
@@ -1,31 +1,45 @@
1
- import { Typography } from "@mui/material";
 
2
 
3
  /**
4
  * A component that renders text with styled words (bold, italic, etc.)
5
  * It automatically handles spacing between words and styled segments
6
  */
7
- export const StyledText = ({
8
- text,
9
- variant = "body1",
10
- component,
11
- ...props
12
- }) => {
13
- // Split the text into segments, preserving spaces
14
- const segments = text.split(/(<strong>.*?<\/strong>)/).filter(Boolean);
15
 
16
  return (
17
- <Typography variant={variant} component={component} {...props}>
18
- {segments.map((segment, index) => {
19
- if (segment.startsWith("<strong>")) {
20
- // Extract the text between <strong> tags and wrap it in <strong>
21
- const content = segment.replace(/<\/?strong>/g, "");
22
- return <strong key={index}>{content}</strong>;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
- // Return regular text segments as is
25
- return segment;
26
  })}
27
- </Typography>
28
  );
29
- };
30
 
31
  export default StyledText;
 
1
+ import { Box, Chip } from "@mui/material";
2
+ import { useGame } from "../contexts/GameContext";
3
 
4
  /**
5
  * A component that renders text with styled words (bold, italic, etc.)
6
  * It automatically handles spacing between words and styled segments
7
  */
8
+ export function StyledText({ text, ...props }) {
9
+ const { heroName } = useGame();
10
+
11
+ if (!text || !heroName) return text;
12
+
13
+ const parts = text.split(new RegExp(`(${heroName})`, "i"));
 
 
14
 
15
  return (
16
+ <Box component="span" sx={{ display: "inline", ...props.sx }}>
17
+ {parts.map((part, index) => {
18
+ if (part.toLowerCase() === heroName.toLowerCase()) {
19
+ return (
20
+ <Chip
21
+ key={index}
22
+ label={part}
23
+ size="small"
24
+ sx={{
25
+ mx: 0.1,
26
+ height: "auto",
27
+ padding: "0px 2px",
28
+ "& .MuiChip-label": {
29
+ padding: "0 2px",
30
+ fontSize: "inherit",
31
+ lineHeight: "inherit",
32
+ fontWeight: "900",
33
+ },
34
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
35
+ }}
36
+ />
37
+ );
38
  }
39
+ return part;
 
40
  })}
41
+ </Box>
42
  );
43
+ }
44
 
45
  export default StyledText;
client/src/layouts/Panel.jsx CHANGED
@@ -9,6 +9,7 @@ import RefreshIcon from "@mui/icons-material/Refresh";
9
  import { useEffect, useState, useRef } from "react";
10
  import { useGame } from "../contexts/GameContext";
11
  import { keyframes } from "@mui/system";
 
12
 
13
  // Animation de rotation complète
14
  const spinFull = keyframes`
@@ -248,20 +249,13 @@ export function Panel({
248
  borderRadius: "8px",
249
  display: "flex",
250
  alignItems: "center",
251
- justifyContent: "center",
252
- boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
 
 
253
  }}
254
  >
255
- <Typography
256
- variant="body1"
257
- sx={{
258
- fontSize: { xs: "0.775rem", sm: "1rem" }, // Responsive font size
259
- color: "black",
260
- lineHeight: 1.2,
261
- }}
262
- >
263
- {segment.text}
264
- </Typography>
265
  </Box>
266
  )}
267
  </Box>
 
9
  import { useEffect, useState, useRef } from "react";
10
  import { useGame } from "../contexts/GameContext";
11
  import { keyframes } from "@mui/system";
12
+ import { StyledText } from "../components/StyledText";
13
 
14
  // Animation de rotation complète
15
  const spinFull = keyframes`
 
249
  borderRadius: "8px",
250
  display: "flex",
251
  alignItems: "center",
252
+ fontSize: { xs: "0.775rem", sm: "1rem" }, // Responsive font size
253
+ color: "black",
254
+ lineHeight: 1.1,
255
+ fontWeight: 900,
256
  }}
257
  >
258
+ <StyledText text={segment.text} />
 
 
 
 
 
 
 
 
 
259
  </Box>
260
  )}
261
  </Box>
client/src/pages/Tutorial.jsx CHANGED
@@ -88,7 +88,7 @@ export function Tutorial() {
88
  the opportunity to write the next part of the story yourself.
89
  <br />
90
  <br />
91
- It's an experimental game. Enjoy!
92
  </Typography>
93
 
94
  <Button
 
88
  the opportunity to write the next part of the story yourself.
89
  <br />
90
  <br />
91
+ Think of it more as a lucid dream than a traditional game. Enjoy!
92
  </Typography>
93
 
94
  <Button
server/core/generators/story_segment_generator.py CHANGED
@@ -60,20 +60,27 @@ Universe Context:
60
  - Genre: {self.universe_genre}
61
  - Epoch: {self.universe_epoch}
62
 
63
- IMPORTANT RULES FOR THE QUEST OBJECT (MANDATORY):
64
- - Most segments must hint at the power of the {self.universe_macguffin}
65
- - Use strong clues ONLY at key moments
66
- - NEVER reveal the full power of the {self.universe_macguffin} before the climax, this is a STRICT limit
67
- - NEVER mention time or place in the story in this manner: [18:00 - a road]
68
-
69
- IMPORTANT RULES FOR STORY TEXT:
70
- - Write ONLY a descriptive narrative text
71
- - DO NOT include any choices, questions, or options
72
- - DO NOT ask what {self.hero_name} should do next
73
- - DO NOT include any dialogue asking for decisions
74
- - Focus purely on describing what is happening in the current scene
75
- - Keep the text concise and impactful
76
- - Use every word purposefully to convey maximum meaning in minimum space
 
 
 
 
 
 
 
77
 
78
  Your task is to generate the next segment of the story, following these rules:
79
  1. Keep the story consistent with the universe parameters
@@ -83,14 +90,22 @@ Your task is to generate the next segment of the story, following these rules:
83
 
84
  Hero Description: {self.hero_desc}
85
 
86
- - MANDATORY: Each segment must be close to 15 words, no exceptions
87
  """
88
 
89
  human_template = """
90
- Current game state:
91
- - Current time: {current_time}
92
- - Current location: {current_location}
93
- - Previous choice: {previous_choice}
 
 
 
 
 
 
 
 
94
 
95
  Story history:
96
  {story_history}
@@ -102,6 +117,10 @@ Be short. Never describes game variables.
102
 
103
  IT MUST BE THE DIRECT CONTINUATION OF THE CURRENT STORY.
104
  You MUST mention the previous situation and what is happening now with the new choice.
 
 
 
 
105
  """
106
  return ChatPromptTemplate(
107
  messages=[
@@ -169,7 +188,7 @@ You MUST mention the previous situation and what is happening now with the new c
169
  def _is_valid_length(self, text: str) -> bool:
170
  """Vérifie si le texte est dans la bonne plage de longueur."""
171
  word_count = len(text.split())
172
- return 15 <= word_count <= 60
173
 
174
  async def generate(self, story_beat: int, current_time: str, current_location: str, previous_choice: str, story_history: str = "", turn_before_end: int = 0, is_winning_story: bool = False) -> StorySegmentResponse:
175
  """Generate the next story segment with length validation and retry."""
@@ -183,17 +202,17 @@ You MUST mention the previous situation and what is happening now with the new c
183
  what_to_represent = self._get_what_to_represent(story_beat, is_death, is_victory)
184
 
185
  # Si c'est un choix personnalisé, on l'utilise comme contexte pour générer la suite
186
- # if previous_choice and not previous_choice.startswith("Choice "):
187
- # what_to_represent = f"""
188
- # Based on the player's custom choice: "{previous_choice}"
189
-
190
- # Write a story segment that:
191
- # 1. Directly follows and incorporates the player's choice
192
- # 2. Maintains consistency with the universe and story
193
- # 3. Respects all previous rules about length and style
194
- # 4. Naturally integrates the custom elements while staying true to the plot
195
- # Close to 15 words.
196
- # """
197
 
198
  # Créer les messages de base une seule fois
199
  messages = self.prompt.format_messages(
@@ -224,9 +243,7 @@ You MUST mention the previous situation and what is happening now with the new c
224
  retry_count += 1
225
  if retry_count < self.max_retries:
226
  # Créer un nouveau message avec le feedback sur la longueur
227
- if word_count < 15:
228
- feedback = f"The previous response was too short ({word_count} words). Here was your last attempt:\n\n{story_text}\n\nPlease generate a NEW and DIFFERENT story segment close to 15 words that continues from: {story_history}"
229
- else:
230
  feedback = f"The previous response was too long ({word_count} words). Here was your last attempt:\n\n{story_text}\n\nPlease generate a MUCH SHORTER story segment close to 15 words that continues from: {story_history}"
231
 
232
  # Réinitialiser les messages avec les messages de base
 
60
  - Genre: {self.universe_genre}
61
  - Epoch: {self.universe_epoch}
62
 
63
+ EXAMPLES:
64
+ - Mateo inspects the relic after choosing to investigate the old house.
65
+ - A young woman finds a hidden door after exploring the alleyway.
66
+ - On a distant planet, an explorer uncovers an artifact after landing.
67
+ - In a medieval village, a blacksmith discovers a map after repairing a sword.
68
+ - A pilot notices a signal after taking a risky shortcut.
69
+ - In a mansion, a detective finds a passage after searching the library.
70
+ - Amidst a market, a thief spots an amulet after blending into the crowd.
71
+ - A diver encounters a ship after exploring uncharted waters.
72
+ - Mateo, the hero, finds a secret compartment after investigating the library.
73
+ - In a city, the hero deciphers a message after hacking the mainframe.
74
+ - A knight finds a hidden passage after examining the castle walls.
75
+ - An astronaut discovers a new planet after navigating through an asteroid field.
76
+ - A scientist uncovers a secret formula after analyzing ancient manuscripts.
77
+ - A warrior finds a mystical weapon after defeating a powerful enemy.
78
+ - A mage discovers a hidden spell after studying ancient runes.
79
+ - A ranger spots a hidden trail after scouting the forest.
80
+ - A sailor finds a treasure map after exploring a deserted island.
81
+ - A spy uncovers a conspiracy after infiltrating the enemy base.
82
+ - A historian finds a lost diary after searching the old archives.
83
+ - A musician discovers a hidden melody after playing an ancient instrument.
84
 
85
  Your task is to generate the next segment of the story, following these rules:
86
  1. Keep the story consistent with the universe parameters
 
90
 
91
  Hero Description: {self.hero_desc}
92
 
93
+ - MANDATORY: Each segment must be close to 15 words, no exceptions.
94
  """
95
 
96
  human_template = """
97
+
98
+ EXAMPLES:
99
+ - Mateo inspects the relic after choosing to investigate the old house.
100
+ - A young woman finds a hidden door after exploring the alleyway.
101
+ - On a distant planet, an explorer uncovers an artifact after landing.
102
+ - In a medieval village, a blacksmith discovers a map after repairing a sword.
103
+ - A pilot notices a signal after taking a risky shortcut.
104
+ - In a mansion, a detective finds a passage after searching the library.
105
+
106
+ BAD:
107
+ - In a mansion, a detective finds a passage after searching the library. [Choix du joueur: Choice 1, "the hero encounter a dragon"]
108
+ - [A town, 00h00] In a medieval village, a blacksmith discovers a map after repairing a sword.
109
 
110
  Story history:
111
  {story_history}
 
117
 
118
  IT MUST BE THE DIRECT CONTINUATION OF THE CURRENT STORY.
119
  You MUST mention the previous situation and what is happening now with the new choice.
120
+ Never propose choices or options. Never describe the game variables.
121
+
122
+ MANDATORY: Each segment must be close to 15 words, keep it concise.
123
+ Be short. Never describes game variables.
124
  """
125
  return ChatPromptTemplate(
126
  messages=[
 
188
  def _is_valid_length(self, text: str) -> bool:
189
  """Vérifie si le texte est dans la bonne plage de longueur."""
190
  word_count = len(text.split())
191
+ return 0 <= word_count <= 30
192
 
193
  async def generate(self, story_beat: int, current_time: str, current_location: str, previous_choice: str, story_history: str = "", turn_before_end: int = 0, is_winning_story: bool = False) -> StorySegmentResponse:
194
  """Generate the next story segment with length validation and retry."""
 
202
  what_to_represent = self._get_what_to_represent(story_beat, is_death, is_victory)
203
 
204
  # Si c'est un choix personnalisé, on l'utilise comme contexte pour générer la suite
205
+ if previous_choice and not previous_choice.startswith("Choice "):
206
+ what_to_represent = f"""
207
+ Based on the player's custom choice: "{previous_choice}"
208
+
209
+ Write a story segment that:
210
+ 1. Directly follows and incorporates the player's choice
211
+ 2. Maintains consistency with the universe and story
212
+ 3. Respects all previous rules about length and style
213
+ 4. Naturally integrates the custom elements while staying true to the plot
214
+ Close to 15 words.
215
+ """
216
 
217
  # Créer les messages de base une seule fois
218
  messages = self.prompt.format_messages(
 
243
  retry_count += 1
244
  if retry_count < self.max_retries:
245
  # Créer un nouveau message avec le feedback sur la longueur
246
+ if word_count > 15:
 
 
247
  feedback = f"The previous response was too long ({word_count} words). Here was your last attempt:\n\n{story_text}\n\nPlease generate a MUCH SHORTER story segment close to 15 words that continues from: {story_history}"
248
 
249
  # Réinitialiser les messages avec les messages de base
server/core/generators/universe_generator.py CHANGED
@@ -16,7 +16,8 @@ class UniverseGenerator(BaseGenerator):
16
  def _create_prompt(self) -> ChatPromptTemplate:
17
 
18
  system_template = """You are a creative writing assistant specialized in comic book universes.
19
- Your task is to rewrite a story while keeping its exact structure and beats, but transposing it into a different universe."""
 
20
 
21
  human_template = """Transform the following story into a new universe with these parameters:
22
  - Visual style: {style_name} (inspired by artists like {artists} with works such as {works})
@@ -51,6 +52,7 @@ The story must be atmospheric, magical, and focus on adventure and discovery. Ea
51
 
52
  YOU HAVE. TOREWRITE THE STORY. ( one text including the constant part and the variable part )
53
  YOU ONLY HAVE TO RIGHT AN INTRODUCTION. SETUP THE STORY AND DEFINE CLEARLY SARASH'S MISSION.
 
54
  """
55
 
56
  return ChatPromptTemplate(
 
16
  def _create_prompt(self) -> ChatPromptTemplate:
17
 
18
  system_template = """You are a creative writing assistant specialized in comic book universes.
19
+ Your task is to rewrite a story while keeping its exact structure and beats, but transposing it into a different universe.
20
+ """
21
 
22
  human_template = """Transform the following story into a new universe with these parameters:
23
  - Visual style: {style_name} (inspired by artists like {artists} with works such as {works})
 
52
 
53
  YOU HAVE. TOREWRITE THE STORY. ( one text including the constant part and the variable part )
54
  YOU ONLY HAVE TO RIGHT AN INTRODUCTION. SETUP THE STORY AND DEFINE CLEARLY SARASH'S MISSION.
55
+
56
  """
57
 
58
  return ChatPromptTemplate(