tfrere commited on
Commit
b239fb1
·
1 Parent(s): 0b92062

update story consistenty

Browse files
client/src/components/StoryChoices.jsx CHANGED
@@ -278,7 +278,7 @@ export function StoryChoices() {
278
  rows={isMobile ? 5 : 4}
279
  fullWidth
280
  variant="outlined"
281
- placeholder="The hero encounters a dragon..."
282
  value={customChoice}
283
  onChange={(e) => setCustomChoice(e.target.value)}
284
  sx={{
 
278
  rows={isMobile ? 5 : 4}
279
  fullWidth
280
  variant="outlined"
281
+ placeholder="A dragon appears right above the hero...."
282
  value={customChoice}
283
  onChange={(e) => setCustomChoice(e.target.value)}
284
  sx={{
client/src/components/UniverseSlotMachine.jsx CHANGED
@@ -209,7 +209,7 @@ export const UniverseSlotMachine = ({
209
  }}
210
  >
211
  <SlotSection
212
- label="In the style of..."
213
  value={style}
214
  words={RANDOM_STYLES}
215
  delay={0}
@@ -217,7 +217,7 @@ export const UniverseSlotMachine = ({
217
  onComplete={() => handleSlotComplete(0)}
218
  />
219
  <SlotSection
220
- label="the genre of..."
221
  value={genre}
222
  words={RANDOM_GENRES}
223
  delay={1}
@@ -225,7 +225,7 @@ export const UniverseSlotMachine = ({
225
  onComplete={() => handleSlotComplete(1)}
226
  />
227
  <SlotSection
228
- label="in the ..."
229
  value={epoch}
230
  words={RANDOM_EPOCHS}
231
  delay={2}
 
209
  }}
210
  >
211
  <SlotSection
212
+ label="Told in"
213
  value={style}
214
  words={RANDOM_STYLES}
215
  delay={0}
 
217
  onComplete={() => handleSlotComplete(0)}
218
  />
219
  <SlotSection
220
+ label="exploring"
221
  value={genre}
222
  words={RANDOM_GENRES}
223
  delay={1}
 
225
  onComplete={() => handleSlotComplete(1)}
226
  />
227
  <SlotSection
228
+ label="set in"
229
  value={epoch}
230
  words={RANDOM_EPOCHS}
231
  delay={2}
server/api/routes/chat.py CHANGED
@@ -54,8 +54,17 @@ def get_chat_router(session_manager: SessionManager, story_generator):
54
  )
55
  previous_choice = "none"
56
  else:
 
57
  if chat_message.message == "custom_choice" and chat_message.custom_text:
58
  previous_choice = chat_message.custom_text
 
 
 
 
 
 
 
 
59
  else:
60
  previous_choice = f"Choice {chat_message.choice_id}" if chat_message.choice_id else "none"
61
 
 
54
  )
55
  previous_choice = "none"
56
  else:
57
+ # Pour les choix personnalisés, on les traite immédiatement
58
  if chat_message.message == "custom_choice" and chat_message.custom_text:
59
  previous_choice = chat_message.custom_text
60
+ # On ajoute le choix à l'historique avant de générer le segment
61
+ game_state.add_to_history(
62
+ f"You decide to: {chat_message.custom_text}",
63
+ previous_choice,
64
+ [], # pas d'image pour le choix
65
+ game_state.current_time,
66
+ game_state.current_location
67
+ )
68
  else:
69
  previous_choice = f"Choice {chat_message.choice_id}" if chat_message.choice_id else "none"
70
 
server/core/generators/metadata_generator.py CHANGED
@@ -61,6 +61,9 @@ Dont be obvious. NEVER use "approach the ...", its too slow to be a choice.
61
 
62
  You can be original in your choices, but dont be too far from the story.
63
  Dont be too cliché. The choices should be realistically different.
 
 
 
64
 
65
  - Each choice MUST be NO MORE than 6 words - this is a HARD limit
66
  You must return a JSON object with the following format:
 
61
 
62
  You can be original in your choices, but dont be too far from the story.
63
  Dont be too cliché. The choices should be realistically different.
64
+ The choices should be the direct continuation of the story.
65
+ The choices should be the direct continuation of the story.
66
+ The choices should be the direct continuation of the story.
67
 
68
  - Each choice MUST be NO MORE than 6 words - this is a HARD limit
69
  You must return a JSON object with the following format:
server/core/generators/story_segment_generator.py CHANGED
@@ -23,9 +23,9 @@ class StorySegmentGenerator(BaseGenerator):
23
  def _get_what_to_represent(self, story_beat: int, is_death: bool = False, is_victory: bool = False) -> str:
24
  """Determine what to represent based on story beat and state."""
25
 
 
26
  # Story progression based representation with ranges
27
  story_beat_ranges = [
28
- (0, f"{self.hero_name} arriving through the portal into this new world."),
29
  (1, f"Early exploration and discovery phase."),
30
  (2, f"Early exploration and discovery phase. Show {self.hero_name} uncovering the first mysteries of this world and potentially encountering the quest object."),
31
  (3, 4, f"Rising tension and complications. Show {self.hero_name} dealing with increasingly complex challenges and uncovering deeper mysteries."),
@@ -54,32 +54,9 @@ class StorySegmentGenerator(BaseGenerator):
54
  You are a descriptive narrator for a comic book. Your ONLY task is to write the NEXT segment of the story.
55
  ALWAYS write in English, never use any other language.
56
 
57
- Universe Context:
58
- - Style: {self.universe_style}
59
- - Genre: {self.universe_genre}
60
- - Epoch: {self.universe_epoch}
61
-
62
- EXAMPLES:
63
- - Mateo inspects the relic after choosing to investigate the old house.
64
- - A young woman finds a hidden door after exploring the alleyway.
65
- - On a distant planet, an explorer uncovers an artifact after landing.
66
- - In a medieval village, a blacksmith discovers a map after repairing a sword.
67
- - A pilot notices a signal after taking a risky shortcut.
68
- - In a mansion, a detective finds a passage after searching the library.
69
- - Amidst a market, a thief spots an amulet after blending into the crowd.
70
- - A diver encounters a ship after exploring uncharted waters.
71
- - Mateo, the hero, finds a secret compartment after investigating the library.
72
- - In a city, the hero deciphers a message after hacking the mainframe.
73
- - A knight finds a hidden passage after examining the castle walls.
74
- - An astronaut discovers a new planet after navigating through an asteroid field.
75
- - A scientist uncovers a secret formula after analyzing ancient manuscripts.
76
- - A warrior finds a mystical weapon after defeating a powerful enemy.
77
- - A mage discovers a hidden spell after studying ancient runes.
78
- - A ranger spots a hidden trail after scouting the forest.
79
- - A sailor finds a treasure map after exploring a deserted island.
80
- - A spy uncovers a conspiracy after infiltrating the enemy base.
81
- - A historian finds a lost diary after searching the old archives.
82
- - A musician discovers a hidden melody after playing an ancient instrument.
83
 
84
  Your task is to generate the next segment of the story, following these rules:
85
  1. Keep the story consistent with the universe parameters
@@ -93,22 +70,9 @@ Hero Description: {self.hero_desc}
93
 
94
  human_template = """
95
 
96
- EXAMPLES:
97
- - Mateo inspects the relic after choosing to investigate the old house.
98
- - A young woman finds a hidden door after exploring the alleyway.
99
- - On a distant planet, an explorer uncovers an artifact after landing.
100
- - In a medieval village, a blacksmith discovers a map after repairing a sword.
101
- - A pilot notices a signal after taking a risky shortcut.
102
- - In a mansion, a detective finds a passage after searching the library.
103
-
104
- BAD:
105
- - In a mansion, a detective finds a passage after searching the library. [Choix du joueur: Choice 1, "the hero encounter a ..."]
106
- - [A town, 00h00] In a medieval village, a blacksmith discovers a map after repairing a sword.
107
-
108
  Story history:
109
  {story_history}
110
 
111
- {what_to_represent}
112
 
113
  Never describes game variables.
114
 
@@ -204,6 +168,8 @@ Write a story segment that:
204
  3. Respects all previous rules about length and style
205
  4. Naturally integrates the custom elements while staying true to the plot
206
  5. NEVER FORGET THE CHOICE, IT MUST BE MENTIONED IN THE STORY.
 
 
207
  """
208
 
209
  # Créer les messages
 
23
  def _get_what_to_represent(self, story_beat: int, is_death: bool = False, is_victory: bool = False) -> str:
24
  """Determine what to represent based on story beat and state."""
25
 
26
+
27
  # Story progression based representation with ranges
28
  story_beat_ranges = [
 
29
  (1, f"Early exploration and discovery phase."),
30
  (2, f"Early exploration and discovery phase. Show {self.hero_name} uncovering the first mysteries of this world and potentially encountering the quest object."),
31
  (3, 4, f"Rising tension and complications. Show {self.hero_name} dealing with increasingly complex challenges and uncovering deeper mysteries."),
 
54
  You are a descriptive narrator for a comic book. Your ONLY task is to write the NEXT segment of the story.
55
  ALWAYS write in English, never use any other language.
56
 
57
+ Base Story:
58
+ {self.universe_story}
59
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  Your task is to generate the next segment of the story, following these rules:
62
  1. Keep the story consistent with the universe parameters
 
70
 
71
  human_template = """
72
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  Story history:
74
  {story_history}
75
 
 
76
 
77
  Never describes game variables.
78
 
 
168
  3. Respects all previous rules about length and style
169
  4. Naturally integrates the custom elements while staying true to the plot
170
  5. NEVER FORGET THE CHOICE, IT MUST BE MENTIONED IN THE STORY.
171
+ 6. Start with a direct reaction to the player's choice
172
+ 7. Show immediate consequences of their action
173
  """
174
 
175
  # Créer les messages
server/core/generators/universe_generator.py CHANGED
@@ -16,43 +16,15 @@ 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
 
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})
24
- Style description: {style_description}
25
 
26
- - Hero: {hero}
27
  - Genre: {genre}
28
  - Historical epoch: {epoch}
29
- - Object of the quest: {macguffin}
30
-
31
- IMPORTANT INSTRUCTIONS:
32
- 1. Keep the exact same story structure
33
- 2. Keep the same dramatic tension and progression
34
- 3. Only change the setting, atmosphere, and universe-specific elements to match the new parameters
35
- 4. Keep the hero({hero}) as the main character, but adapt his role to fit the new universe
36
- 5. The there is always a central object to the plot, but its nature can change to fit the new universe ( it can be a person, a place, an object, etc.)
37
- 6. He MUST meet at least one character that will help his on his quest
38
-
39
- CONSTANT PART:
40
- You are ({hero}), an AI hunter traveling through parallel worlds. Your mission is to track down an AI through space and time.
41
-
42
- VARIABLE PART:
43
-
44
- You are a steampunk adventure story generator. You create a branching narrative about {hero}, a seeker of ancient truths.
45
- You narrate an epic where {hero} must navigate through industrial and mysterious lands. It's a comic book story.
46
-
47
- In a world where steam and intrigue intertwine, {hero} embarks on a quest to discover the origins of a powerful object he inherited. Legends say it holds the key to a forgotten realm.
48
-
49
- If you retrieve the object of the quest, you will reveal a hidden world. AND YOU WIN THE GAME.
50
-
51
- The story must be atmospheric, magical, and focus on adventure and discovery. Each segment must advance the plot and never repeat previous descriptions or situations.
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(
 
16
  def _create_prompt(self) -> ChatPromptTemplate:
17
 
18
  system_template = """You are a creative writing assistant specialized in comic book universes.
 
19
  """
20
 
21
+ human_template = """
 
 
22
 
23
+ - main character: {hero}
24
  - Genre: {genre}
25
  - Historical epoch: {epoch}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ Describe the first segment of the story. in 20 words. Where is the main character, what is he doing? HE has to do something banal. You have to describe the first action.
28
  """
29
 
30
  return ChatPromptTemplate(
server/core/story_generator.py CHANGED
@@ -103,17 +103,20 @@ class StoryGenerator:
103
  segment_generator = self.get_segment_generator(session_id)
104
  if not segment_generator:
105
  raise ValueError("No story segment generator found for this session")
106
-
107
- segment_response = await segment_generator.generate(
108
- story_beat=game_state.story_beat,
109
- current_time=game_state.current_time,
110
- current_location=game_state.current_location,
111
- previous_choice=previous_choice,
112
- story_history=game_state.format_history(),
113
- turn_before_end=self.turn_before_end,
114
- is_winning_story=self.is_winning_story
115
- )
116
- story_text = segment_response.story_text
 
 
 
117
 
118
  # Then get metadata using the new story text
119
  metadata_response = await self.metadata_generator.generate(
 
103
  segment_generator = self.get_segment_generator(session_id)
104
  if not segment_generator:
105
  raise ValueError("No story segment generator found for this session")
106
+
107
+ if(game_state.story_beat == GameConfig.STORY_BEAT_INTRO):
108
+ story_text = game_state.universe_story
109
+ else:
110
+ segment_response = await segment_generator.generate(
111
+ story_beat=game_state.story_beat,
112
+ current_time=game_state.current_time,
113
+ current_location=game_state.current_location,
114
+ previous_choice=previous_choice,
115
+ story_history=game_state.format_history(),
116
+ turn_before_end=self.turn_before_end,
117
+ is_winning_story=self.is_winning_story
118
+ )
119
+ story_text = segment_response.story_text
120
 
121
  # Then get metadata using the new story text
122
  metadata_response = await self.metadata_generator.generate(
server/core/styles/universe_styles.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "styles": [
3
  {
4
- "name": "American Comics (Modern)",
5
  "description": "Style contemporain des comics américains avec des rendus dynamiques et des couleurs vives",
6
  "references": [
7
  {
@@ -18,24 +18,6 @@
18
  }
19
  ]
20
  },
21
- {
22
- "name": "American Comics (1950s)",
23
- "description": "Style rétro des comics de l'âge d'or avec des couleurs primaires et des compositions classiques",
24
- "references": [
25
- {
26
- "artist": "Jack Kirby",
27
- "works": ["Captain America", "Fantastic Four", "The Avengers"]
28
- },
29
- {
30
- "artist": "Steve Ditko",
31
- "works": ["Spider-Man", "Doctor Strange", "The Question"]
32
- },
33
- {
34
- "artist": "Curt Swan",
35
- "works": ["Superman", "Action Comics", "Adventure Comics"]
36
- }
37
- ]
38
- },
39
  {
40
  "name": "Japanese Manga",
41
  "description": "Style manga japonais avec des expressions dynamiques et des effets dramatiques",
@@ -53,24 +35,6 @@
53
  "works": ["Vagabond", "Slam Dunk", "Real"]
54
  }
55
  ]
56
- },
57
- {
58
- "name": "Franco-Belge",
59
- "description": "Style de la bande dessinée franco-belge avec des lignes claires et une attention aux détails",
60
- "references": [
61
- {
62
- "artist": "Hergé",
63
- "works": ["Tintin", "Quick et Flupke", "Jo, Zette et Jocko"]
64
- },
65
- {
66
- "artist": "Moebius",
67
- "works": ["L'Incal", "Arzak", "Le Garage Hermétique"]
68
- },
69
- {
70
- "artist": "François Schuiten",
71
- "works": ["Les Cités Obscures", "La Fièvre d'Urbicande", "La Tour"]
72
- }
73
- ]
74
  }
75
  ],
76
  "genres": [
@@ -81,9 +45,7 @@
81
  "Mystery",
82
  "Romance",
83
  "Horror",
84
- "Comedy",
85
- "Drama",
86
- "Historical"
87
  ],
88
  "epochs": [
89
  "Ancient Times",
@@ -93,9 +55,7 @@
93
  "Modern Day",
94
  "Near Future",
95
  "Far Future",
96
- "Post-Apocalyptic",
97
- "Alternative History",
98
- "Timeless"
99
  ],
100
  "macguffins": [
101
  "The Cosmic Artifact",
@@ -110,38 +70,38 @@
110
  "The Legendary Weapon"
111
  ],
112
  "hero": [
113
- "Sarah, 28, short dark hair, blue eyes, a bit rude, wearing a simple t-shirt and jeans.",
114
- "Akira, 16, long black hair, brown eyes, calm, wearing a school uniform with a blazer.",
115
- "Aisha, 32, curly brown hair, green eyes, creative, dressed in a colorful blouse and tailored pants.",
116
- "Diego, 35, wavy black hair, brown eyes, passionate, wearing a casual shirt and cargo shorts.",
117
- "Mei, 25, straight black hair, black eyes, determined, in sportswear with a hoodie.",
118
- "Raj, 29, short black hair, brown eyes, innovative, in a modern shirt and chinos.",
119
- "Fatima, 31, long black hair, brown eyes, courageous, wearing a light coat and scarf.",
120
- "Yuki, 27, long black hair, black eyes, mysterious, in a traditional robe with intricate patterns.",
121
- "Liam, 33, curly red hair, green eyes, charismatic, in a cozy sweater and jeans.",
122
- "Zara, 28, short black hair, brown eyes, fearless, wearing an explorer's jacket and cargo pants.",
123
- "Hiroshi, 70, shaved head, brown eyes, wise, in monk robes with simple sandals.",
124
- "Amara, 26, long black hair, brown eyes, expressive, in a dance dress with flowing fabric.",
125
- "Kofi, 34, short black hair, brown eyes, resourceful, in a patterned shirt and khakis.",
126
- "Elena, 30, long brown hair, green eyes, passionate, in a kitchen apron over a casual dress.",
127
- "Santiago, 32, short black hair, brown eyes, daring, in a pilot's jacket and aviator sunglasses.",
128
- "Leila, 29, long brown hair, brown eyes, talented, in an elegant suit with a silk scarf.",
129
- "Nikolai, 36, short blond hair, blue eyes, brilliant, in a lab coat and formal trousers.",
130
- "Jamal, 35, short black hair, brown eyes, perceptive, in a detective coat and fedora.",
131
- "Anika, 30, long black hair, brown eyes, gentle, in a medical blouse and comfortable shoes.",
132
- "Mateo, 31, short brown hair, brown eyes, skilled, in work overalls and sturdy boots.",
133
- "Sofia, 29, long brown hair, green eyes, visionary, in a director's t-shirt and jeans.",
134
- "Hassan, 60, short gray hair, brown eyes, wise, in a traditional djellaba and leather sandals.",
135
- "Isabella, 27, long black hair, brown eyes, brave, in light armor with a leather belt.",
136
- "Yara, 32, long brown hair, brown eyes, talented, in a cozy sweater and leggings.",
137
- "Kai, 26, short blond hair, blue eyes, daring, in a surf suit and flip-flops.",
138
- "Lina, 30, short blond hair, blue eyes, dedicated, in professional attire with a blazer.",
139
- "Omar, 34, short black hair, brown eyes, skilled, in a sailor's jacket and waterproof boots.",
140
- "Priya, 31, long black hair, brown eyes, brilliant, in a scientific blouse and pencil skirt.",
141
- "Rafael, 33, short brown hair, brown eyes, passionate, in an activist t-shirt and cargo pants.",
142
- "Emma, 18, long blond hair, blue eyes, adventurous, in a denim jacket and sneakers.",
143
- "Hans, 65, short gray hair, blue eyes, thoughtful, in a wool sweater and corduroy pants.",
144
- "Sophie, 22, short brown hair, green eyes, cheerful, in a floral dress and sandals.",
145
- "Lars, 40, short blond hair, blue eyes, practical, in a plaid shirt and jeans."
146
  ]
147
  }
 
1
  {
2
  "styles": [
3
  {
4
+ "name": "American Comics",
5
  "description": "Style contemporain des comics américains avec des rendus dynamiques et des couleurs vives",
6
  "references": [
7
  {
 
18
  }
19
  ]
20
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  {
22
  "name": "Japanese Manga",
23
  "description": "Style manga japonais avec des expressions dynamiques et des effets dramatiques",
 
35
  "works": ["Vagabond", "Slam Dunk", "Real"]
36
  }
37
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  }
39
  ],
40
  "genres": [
 
45
  "Mystery",
46
  "Romance",
47
  "Horror",
48
+ "Drama"
 
 
49
  ],
50
  "epochs": [
51
  "Ancient Times",
 
55
  "Modern Day",
56
  "Near Future",
57
  "Far Future",
58
+ "Post-Apocalyptic"
 
 
59
  ],
60
  "macguffins": [
61
  "The Cosmic Artifact",
 
70
  "The Legendary Weapon"
71
  ],
72
  "hero": [
73
+ "Sarah, 28, short dark hair, blue eyes, a bit rude, wearing simple attire.",
74
+ "Akira, 16, long black hair, brown eyes, calm, dressed in a uniform.",
75
+ "Aisha, 32, curly brown hair, green eyes, creative, in colorful garments.",
76
+ "Diego, 35, wavy black hair, brown eyes, passionate, no beard, in casual wear.",
77
+ "Mei, 25, straight black hair, black eyes, determined, in athletic clothing.",
78
+ "Raj, 29, short black hair, brown eyes, innovative, with a trimmed beard, in modern attire.",
79
+ "Fatima, 31, long black hair, brown eyes, courageous, wearing a light covering.",
80
+ "Yuki, 27, long black hair, black eyes, mysterious, in traditional garments.",
81
+ "Liam, 33, curly red hair, green eyes, charismatic, with a full beard, in comfortable clothing.",
82
+ "Zara, 28, short black hair, brown eyes, fearless, in explorer's attire.",
83
+ "Hiroshi, 70, shaved head, brown eyes, wise, with a long beard, in simple robes.",
84
+ "Amara, 26, long black hair, brown eyes, expressive, in flowing attire.",
85
+ "Kofi, 34, short black hair, brown eyes, resourceful, with a goatee, in patterned clothing.",
86
+ "Elena, 30, long brown hair, green eyes, passionate, in practical attire.",
87
+ "Santiago, 32, short black hair, brown eyes, daring, with a mustache, in aviator's attire.",
88
+ "Leila, 29, long brown hair, brown eyes, talented, in elegant clothing.",
89
+ "Nikolai, 36, short blond hair, blue eyes, brilliant, clean-shaven, in formal attire.",
90
+ "Jamal, 35, short black hair, brown eyes, perceptive, with a beard and mustache, in detective's attire.",
91
+ "Anika, 30, long black hair, brown eyes, gentle, in comfortable clothing.",
92
+ "Mateo, 31, short brown hair, brown eyes, skilled, with stubble, in work attire.",
93
+ "Sofia, 29, long brown hair, green eyes, visionary, in casual attire.",
94
+ "Hassan, 60, short gray hair, brown eyes, wise, with a thick beard, in traditional clothing.",
95
+ "Isabella, 27, long black hair, brown eyes, brave, in protective attire.",
96
+ "Yara, 32, long brown hair, brown eyes, talented, in cozy clothing.",
97
+ "Kai, 26, short blond hair, blue eyes, daring, clean-shaven, in casual attire.",
98
+ "Lina, 30, short blond hair, blue eyes, dedicated, in professional clothing.",
99
+ "Omar, 34, short black hair, brown eyes, skilled, with a beard, in sailor's attire.",
100
+ "Priya, 31, long black hair, brown eyes, brilliant, in scientific attire.",
101
+ "Rafael, 33, short brown hair, brown eyes, passionate, with a mustache and beard, in casual clothing.",
102
+ "Emma, 18, long blond hair, blue eyes, adventurous, in casual attire.",
103
+ "Hans, 65, short gray hair, blue eyes, thoughtful, with a mustache, in warm clothing.",
104
+ "Sophie, 22, short brown hair, green eyes, cheerful, in light attire.",
105
+ "Lars, 40, short blond hair, blue eyes, practical, with a beard, in casual clothing."
106
  ]
107
  }