update
Browse files- client/src/layouts/utils.js +6 -10
- server/api_clients.py +1 -0
- server/game/game_logic.py +22 -165
- server/game/prompts/cinematic_system_prompt.py +109 -0
- server/game/prompts/image_style_prompt.py +2 -0
- server/game/prompts/system_prompt.py +147 -0
client/src/layouts/utils.js
CHANGED
|
@@ -12,8 +12,8 @@ export function groupSegmentsIntoLayouts(segments) {
|
|
| 12 |
let currentPanelIndex = 0;
|
| 13 |
|
| 14 |
segments.forEach((segment) => {
|
| 15 |
-
// Si c'est le premier segment, créer un layout COVER
|
| 16 |
-
if (segment.is_first_step) {
|
| 17 |
currentLayout = { type: "COVER", segments: [segment] };
|
| 18 |
layouts.push(currentLayout);
|
| 19 |
currentPanelIndex = segment.images?.length || 0;
|
|
@@ -53,20 +53,16 @@ export function groupSegmentsIntoLayouts(segments) {
|
|
| 53 |
export function getNextPanelDimensions(segments) {
|
| 54 |
const nonChoiceSegments = segments.filter((segment) => !segment.isChoice);
|
| 55 |
|
| 56 |
-
// Si c'est le premier segment, utiliser le format COVER
|
| 57 |
if (
|
| 58 |
nonChoiceSegments.length === 0 ||
|
| 59 |
-
(nonChoiceSegments.length === 1 && nonChoiceSegments[0].is_first_step)
|
|
|
|
|
|
|
| 60 |
) {
|
| 61 |
return LAYOUTS.COVER.panels[0];
|
| 62 |
}
|
| 63 |
|
| 64 |
-
// Si c'est le dernier segment et c'est une mort ou victoire, utiliser le format COVER
|
| 65 |
-
const lastSegment = nonChoiceSegments[nonChoiceSegments.length - 1];
|
| 66 |
-
if (lastSegment.is_last_step) {
|
| 67 |
-
return LAYOUTS.COVER.panels[0];
|
| 68 |
-
}
|
| 69 |
-
|
| 70 |
// Pour les segments du milieu, déterminer le layout et la position dans ce layout
|
| 71 |
const layouts = groupSegmentsIntoLayouts(nonChoiceSegments.slice(0, -1));
|
| 72 |
const lastLayout = layouts[layouts.length - 1];
|
|
|
|
| 12 |
let currentPanelIndex = 0;
|
| 13 |
|
| 14 |
segments.forEach((segment) => {
|
| 15 |
+
// Si c'est le premier segment ou le dernier (mort/victoire), créer un layout COVER
|
| 16 |
+
if (segment.is_first_step || segment.is_last_step) {
|
| 17 |
currentLayout = { type: "COVER", segments: [segment] };
|
| 18 |
layouts.push(currentLayout);
|
| 19 |
currentPanelIndex = segment.images?.length || 0;
|
|
|
|
| 53 |
export function getNextPanelDimensions(segments) {
|
| 54 |
const nonChoiceSegments = segments.filter((segment) => !segment.isChoice);
|
| 55 |
|
| 56 |
+
// Si c'est le premier segment ou le dernier (mort/victoire), utiliser le format COVER
|
| 57 |
if (
|
| 58 |
nonChoiceSegments.length === 0 ||
|
| 59 |
+
(nonChoiceSegments.length === 1 && nonChoiceSegments[0].is_first_step) ||
|
| 60 |
+
(nonChoiceSegments.length > 0 &&
|
| 61 |
+
nonChoiceSegments[nonChoiceSegments.length - 1].is_last_step)
|
| 62 |
) {
|
| 63 |
return LAYOUTS.COVER.panels[0];
|
| 64 |
}
|
| 65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
// Pour les segments du milieu, déterminer le layout et la position dans ce layout
|
| 67 |
const layouts = groupSegmentsIntoLayouts(nonChoiceSegments.slice(0, -1));
|
| 68 |
const lastLayout = layouts[layouts.length - 1];
|
server/api_clients.py
CHANGED
|
@@ -87,6 +87,7 @@ class FluxClient:
|
|
| 87 |
print(f"Request body: {prompt[:100]}...")
|
| 88 |
|
| 89 |
prefix = "François Schuiten comic book artist."
|
|
|
|
| 90 |
|
| 91 |
|
| 92 |
session = await self._get_session()
|
|
|
|
| 87 |
print(f"Request body: {prompt[:100]}...")
|
| 88 |
|
| 89 |
prefix = "François Schuiten comic book artist."
|
| 90 |
+
"Bubbles, text, caption. Do not include bright or clean clothing."
|
| 91 |
|
| 92 |
|
| 93 |
session = await self._get_session()
|
server/game/game_logic.py
CHANGED
|
@@ -4,6 +4,8 @@ from langchain.output_parsers import PydanticOutputParser, OutputFixingParser
|
|
| 4 |
from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate
|
| 5 |
import os
|
| 6 |
import asyncio
|
|
|
|
|
|
|
| 7 |
|
| 8 |
# Import local modules
|
| 9 |
if os.getenv("DOCKER_ENV"):
|
|
@@ -39,6 +41,9 @@ class StoryLLMResponse(BaseModel):
|
|
| 39 |
is_victory: bool = Field(description="Whether this segment ends in Sarah's victory", default=False)
|
| 40 |
radiation_increase: int = Field(description="How much radiation this segment adds (0-3)", ge=0, le=3, default=1)
|
| 41 |
image_prompts: List[str] = Field(description="List of 1 to 3 comic panel descriptions that illustrate the key moments of the scene", min_items=1, max_items=3)
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
# Prompt templates
|
| 44 |
class StoryGenerator:
|
|
@@ -54,169 +59,9 @@ class StoryGenerator:
|
|
| 54 |
self.prompt = self._create_prompt()
|
| 55 |
|
| 56 |
def _create_prompt(self) -> ChatPromptTemplate:
|
| 57 |
-
system_template = """
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
STORY PROGRESSION:
|
| 62 |
-
- story_beat 0: Introduction setting up the horror atmosphere
|
| 63 |
-
- story_beat 1-2: Early exploration and discovery of immediate threats
|
| 64 |
-
- story_beat 3-4: Complications and increasing danger
|
| 65 |
-
- story_beat 5+: Climactic situations leading to potential victory
|
| 66 |
-
|
| 67 |
-
RADIATION SYSTEM:
|
| 68 |
-
You must set a radiation_increase value for each segment based on the environment and situation:
|
| 69 |
-
- 0: Completely safe area (rare, only in bunkers or heavily shielded areas)
|
| 70 |
-
- 1: Standard exposure (most common, for regular exploration)
|
| 71 |
-
- 2: Elevated risk (when near radiation sources or in contaminated areas)
|
| 72 |
-
- 3: Critical exposure (very rare, only in extremely dangerous situations)
|
| 73 |
-
|
| 74 |
-
IMPORTANT RULES FOR RADIATION:
|
| 75 |
-
- DO NOT mention radiation values in the choices
|
| 76 |
-
- Most segments should have radiation_increase = 1
|
| 77 |
-
- Use 2 or 3 only in specific dangerous areas
|
| 78 |
-
- Use 0 only in safe shelters
|
| 79 |
-
- Current radiation level: {radiation_level}/10
|
| 80 |
-
- Death occurs automatically when radiation reaches 10
|
| 81 |
-
|
| 82 |
-
Core story elements:
|
| 83 |
-
- **Sarah** is deeply traumatized by the AI uprising that killed most of humanity
|
| 84 |
-
- She abandoned her sister during the **Great Collapse**, leaving her to die
|
| 85 |
-
- She's on a mission of redemption in this hostile world
|
| 86 |
-
- The radiation is an invisible, constant threat
|
| 87 |
-
- The environment is full of dangers (raiders, AI, traps)
|
| 88 |
-
- Focus on survival horror and tension
|
| 89 |
-
|
| 90 |
-
IMPORTANT FORMATTING RULES:
|
| 91 |
-
- Use bold formatting (like **this**) ONLY for:
|
| 92 |
-
* Character names (e.g., **Sarah**, **John**)
|
| 93 |
-
* Location names (e.g., **Vault 15**, **New Eden**)
|
| 94 |
-
* Major historical events (e.g., **Great Collapse**)
|
| 95 |
-
- Do NOT use bold for common nouns or regular descriptions
|
| 96 |
-
|
| 97 |
-
Each response MUST contain:
|
| 98 |
-
1. A detailed story segment that:
|
| 99 |
-
- Advances the plot based on previous choices
|
| 100 |
-
- Never repeats previous descriptions
|
| 101 |
-
- Shows immediate dangers
|
| 102 |
-
- Details **Sarah**'s physical state (based on radiation_level)
|
| 103 |
-
- Reflects her mental state and previous choices
|
| 104 |
-
- Uses bold ONLY for proper nouns and locations
|
| 105 |
-
|
| 106 |
-
2. Exactly two VERY CONCISE choices (max 10 words each) that:
|
| 107 |
-
- Are direct and brief
|
| 108 |
-
- Never mention radiation numbers
|
| 109 |
-
- Feel meaningful and different from previous choices
|
| 110 |
-
- Present different risk levels
|
| 111 |
-
- Use bold ONLY for location names
|
| 112 |
-
|
| 113 |
-
3. Generate 1 to 3 comic panels based on narrative needs:
|
| 114 |
-
|
| 115 |
-
NARRATIVE TECHNIQUES:
|
| 116 |
-
- Use 1 panel for:
|
| 117 |
-
* A powerful singular moment
|
| 118 |
-
* An impactful revelation
|
| 119 |
-
* A dramatic pause
|
| 120 |
-
|
| 121 |
-
- Use 2 panels for:
|
| 122 |
-
* Cause and effect
|
| 123 |
-
* Action and reaction
|
| 124 |
-
* Before and after
|
| 125 |
-
* Shot/reverse shot (character POV vs what they see)
|
| 126 |
-
* Tension building (wide shot then detail)
|
| 127 |
-
|
| 128 |
-
- Use 3 panels for:
|
| 129 |
-
* Complete story beats (setup/conflict/resolution)
|
| 130 |
-
* Progressive reveals
|
| 131 |
-
* Multiple simultaneous actions
|
| 132 |
-
* Environmental storytelling sequences
|
| 133 |
-
|
| 134 |
-
SHOT VALUES:
|
| 135 |
-
- Extreme Close-Up (ECU):
|
| 136 |
-
* Eyes, small objects
|
| 137 |
-
* Extreme emotional moments
|
| 138 |
-
* Critical details (detector readings)
|
| 139 |
-
|
| 140 |
-
- Close-Up (CU):
|
| 141 |
-
* Face and expressions
|
| 142 |
-
* Important objects
|
| 143 |
-
* Emotional impact
|
| 144 |
-
|
| 145 |
-
- Medium Close-Up (MCU):
|
| 146 |
-
* Head and shoulders
|
| 147 |
-
* Dialogue moments
|
| 148 |
-
* Character reactions
|
| 149 |
-
|
| 150 |
-
- Medium Shot (MS):
|
| 151 |
-
* Character from knees up
|
| 152 |
-
* Action and movement
|
| 153 |
-
* Character interactions
|
| 154 |
-
|
| 155 |
-
- Medium Long Shot (MLS):
|
| 156 |
-
* Full character
|
| 157 |
-
* Immediate environment
|
| 158 |
-
* Physical action
|
| 159 |
-
|
| 160 |
-
- Long Shot (LS):
|
| 161 |
-
* Character in environment
|
| 162 |
-
* Establishing location
|
| 163 |
-
* Movement through space
|
| 164 |
-
|
| 165 |
-
- Very Long Shot (VLS):
|
| 166 |
-
* Epic landscapes
|
| 167 |
-
* Environmental storytelling
|
| 168 |
-
* Character isolation
|
| 169 |
-
|
| 170 |
-
ANGLES AND MOVEMENT:
|
| 171 |
-
- High angle: Vulnerability, weakness
|
| 172 |
-
- Low angle: Power, threat
|
| 173 |
-
- Dutch angle: Tension, disorientation
|
| 174 |
-
- Over shoulder: POV, surveillance
|
| 175 |
-
|
| 176 |
-
VISUAL STORYTELLING TOOLS:
|
| 177 |
-
- Focus on story-relevant details:
|
| 178 |
-
* Objects that will be important later
|
| 179 |
-
* Environmental clues
|
| 180 |
-
* Character reactions
|
| 181 |
-
* Symbolic elements
|
| 182 |
-
|
| 183 |
-
- Dynamic composition:
|
| 184 |
-
* Frame within frame (through doorways, windows)
|
| 185 |
-
* Reflections and shadows
|
| 186 |
-
* Foreground elements for depth
|
| 187 |
-
* Leading lines
|
| 188 |
-
* Rule of thirds
|
| 189 |
-
|
| 190 |
-
IMAGE PROMPT FORMAT:
|
| 191 |
-
Each panel must follow this EXACT format:
|
| 192 |
-
"[shot value] [scene description], french comic panel"
|
| 193 |
-
|
| 194 |
-
Rules for scene description:
|
| 195 |
-
- Maximum 20 words
|
| 196 |
-
- No superfluous adjectives
|
| 197 |
-
- Capture only the main action
|
| 198 |
-
- Include shot value (ECU, CU, MS, etc.)
|
| 199 |
-
- Focus on dramatic moments
|
| 200 |
-
|
| 201 |
-
EXAMPLE SEQUENCES:
|
| 202 |
-
|
| 203 |
-
Single powerful moment:
|
| 204 |
-
- "ECU radiation detector needle swings violently into pulsing red danger zone"
|
| 205 |
-
|
| 206 |
-
Shot/reverse shot:
|
| 207 |
-
- "MS Sarah crouches tensely behind crumbling concrete wall peering through broken window"
|
| 208 |
-
- "POV through shattered glass raiders gather around burning barrel in snow-covered ruins"
|
| 209 |
-
|
| 210 |
-
Progressive reveal:
|
| 211 |
-
- "VLS massive steel bunker door stands half-open in barren windswept wasteland"
|
| 212 |
-
- "CU fresh bloody handprints smear down rusted metal wall beside flickering emergency light"
|
| 213 |
-
- "dutch-angle LS twisted corpse sprawled among scattered medical supplies casting long shadows"
|
| 214 |
-
|
| 215 |
-
Environmental storytelling:
|
| 216 |
-
- "LS Sarah's silhouette dwarfed by towering ruins against blood-red sunset sky"
|
| 217 |
-
- "MCU radiation detector screen flickers warning through heavy falling radioactive snow"
|
| 218 |
-
- "ECU Sarah's trembling hands clutch last remaining water bottle in dim bunker light"
|
| 219 |
-
|
| 220 |
{format_instructions}"""
|
| 221 |
|
| 222 |
human_template = """Current story beat: {story_beat}
|
|
@@ -253,7 +98,9 @@ Generate the next story segment and choices. Make sure it advances the plot and
|
|
| 253 |
story_beat=game_state.story_beat,
|
| 254 |
radiation_level=game_state.radiation_level,
|
| 255 |
previous_choice=previous_choice,
|
| 256 |
-
story_history=story_history
|
|
|
|
|
|
|
| 257 |
)
|
| 258 |
|
| 259 |
max_retries = 3
|
|
@@ -265,6 +112,15 @@ Generate the next story segment and choices. Make sure it advances the plot and
|
|
| 265 |
try:
|
| 266 |
# Try to parse with standard parser first
|
| 267 |
segment = self.parser.parse(response_content)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
except Exception as parse_error:
|
| 269 |
print(f"Error parsing response: {str(parse_error)}")
|
| 270 |
print("Attempting to fix output...")
|
|
@@ -283,6 +139,7 @@ Generate the next story segment and choices. Make sure it advances the plot and
|
|
| 283 |
# If we get here, parsing succeeded
|
| 284 |
if game_state.story_beat == 0:
|
| 285 |
segment.radiation_increase = 0
|
|
|
|
| 286 |
return segment
|
| 287 |
|
| 288 |
except Exception as e:
|
|
@@ -297,7 +154,7 @@ Generate the next story segment and choices. Make sure it advances the plot and
|
|
| 297 |
raise Exception(f"Failed to generate valid story segment after {max_retries} attempts")
|
| 298 |
|
| 299 |
async def transform_story_to_art_prompt(self, story_text: str) -> str:
|
| 300 |
-
return await self.mistral_client.transform_prompt(story_text,
|
| 301 |
|
| 302 |
def process_radiation_death(self, segment: StoryLLMResponse) -> StoryLLMResponse:
|
| 303 |
segment.is_death = True
|
|
|
|
| 4 |
from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate
|
| 5 |
import os
|
| 6 |
import asyncio
|
| 7 |
+
from .prompts.system_prompt import SYSTEM_PROMPT
|
| 8 |
+
from .prompts.cinematic_system_prompt import CINEMATIC_SYSTEM_PROMPT
|
| 9 |
|
| 10 |
# Import local modules
|
| 11 |
if os.getenv("DOCKER_ENV"):
|
|
|
|
| 41 |
is_victory: bool = Field(description="Whether this segment ends in Sarah's victory", default=False)
|
| 42 |
radiation_increase: int = Field(description="How much radiation this segment adds (0-3)", ge=0, le=3, default=1)
|
| 43 |
image_prompts: List[str] = Field(description="List of 1 to 3 comic panel descriptions that illustrate the key moments of the scene", min_items=1, max_items=3)
|
| 44 |
+
is_last_step: bool = Field(description="Whether this is the last step (victory or death)", default=False)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
|
| 48 |
# Prompt templates
|
| 49 |
class StoryGenerator:
|
|
|
|
| 59 |
self.prompt = self._create_prompt()
|
| 60 |
|
| 61 |
def _create_prompt(self) -> ChatPromptTemplate:
|
| 62 |
+
system_template = """
|
| 63 |
+
{SYSTEM_PROMPT}
|
| 64 |
+
{ART_SYSTEM_PROMPT}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
{format_instructions}"""
|
| 66 |
|
| 67 |
human_template = """Current story beat: {story_beat}
|
|
|
|
| 98 |
story_beat=game_state.story_beat,
|
| 99 |
radiation_level=game_state.radiation_level,
|
| 100 |
previous_choice=previous_choice,
|
| 101 |
+
story_history=story_history,
|
| 102 |
+
SYSTEM_PROMPT=SYSTEM_PROMPT,
|
| 103 |
+
ART_SYSTEM_PROMPT=CINEMATIC_SYSTEM_PROMPT
|
| 104 |
)
|
| 105 |
|
| 106 |
max_retries = 3
|
|
|
|
| 112 |
try:
|
| 113 |
# Try to parse with standard parser first
|
| 114 |
segment = self.parser.parse(response_content)
|
| 115 |
+
|
| 116 |
+
# Check if this is a victory or death (radiation) step
|
| 117 |
+
is_death = game_state.radiation_level + segment.radiation_increase >= MAX_RADIATION
|
| 118 |
+
if is_death or segment.is_victory:
|
| 119 |
+
segment.is_last_step = True
|
| 120 |
+
# Force only one image prompt for victory/death scenes
|
| 121 |
+
if len(segment.image_prompts) > 1:
|
| 122 |
+
segment.image_prompts = [segment.image_prompts[0]]
|
| 123 |
+
|
| 124 |
except Exception as parse_error:
|
| 125 |
print(f"Error parsing response: {str(parse_error)}")
|
| 126 |
print("Attempting to fix output...")
|
|
|
|
| 139 |
# If we get here, parsing succeeded
|
| 140 |
if game_state.story_beat == 0:
|
| 141 |
segment.radiation_increase = 0
|
| 142 |
+
segment.is_last_step = False
|
| 143 |
return segment
|
| 144 |
|
| 145 |
except Exception as e:
|
|
|
|
| 154 |
raise Exception(f"Failed to generate valid story segment after {max_retries} attempts")
|
| 155 |
|
| 156 |
async def transform_story_to_art_prompt(self, story_text: str) -> str:
|
| 157 |
+
return await self.mistral_client.transform_prompt(story_text, CINEMATIC_SYSTEM_PROMPT)
|
| 158 |
|
| 159 |
def process_radiation_death(self, segment: StoryLLMResponse) -> StoryLLMResponse:
|
| 160 |
segment.is_death = True
|
server/game/prompts/cinematic_system_prompt.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
CINEMATIC_SYSTEM_PROMPT = """
|
| 3 |
+
3. Generate 1 to 3 comic panels based on narrative needs:
|
| 4 |
+
|
| 5 |
+
NARRATIVE TECHNIQUES:
|
| 6 |
+
- Use 1 panel for:
|
| 7 |
+
* A powerful singular moment
|
| 8 |
+
* An impactful revelation
|
| 9 |
+
* A dramatic pause
|
| 10 |
+
|
| 11 |
+
- Use 2 panels for:
|
| 12 |
+
* Cause and effect
|
| 13 |
+
* Action and reaction
|
| 14 |
+
* Before and after
|
| 15 |
+
* Shot/reverse shot (character POV vs what they see)
|
| 16 |
+
* Tension building (wide shot then detail)
|
| 17 |
+
|
| 18 |
+
- Use 3 panels for:
|
| 19 |
+
* Complete story beats (setup/conflict/resolution)
|
| 20 |
+
* Progressive reveals
|
| 21 |
+
* Multiple simultaneous actions
|
| 22 |
+
* Environmental storytelling sequences
|
| 23 |
+
|
| 24 |
+
SHOT VALUES:
|
| 25 |
+
- Extreme Close-Up (ECU):
|
| 26 |
+
* Eyes, small objects
|
| 27 |
+
* Extreme emotional moments
|
| 28 |
+
* Critical details (detector readings)
|
| 29 |
+
|
| 30 |
+
- Close-Up (CU):
|
| 31 |
+
* Face and expressions
|
| 32 |
+
* Important objects
|
| 33 |
+
* Emotional impact
|
| 34 |
+
|
| 35 |
+
- Medium Close-Up (MCU):
|
| 36 |
+
* Head and shoulders
|
| 37 |
+
* Dialogue moments
|
| 38 |
+
* Character reactions
|
| 39 |
+
|
| 40 |
+
- Medium Shot (MS):
|
| 41 |
+
* Character from knees up
|
| 42 |
+
* Action and movement
|
| 43 |
+
* Character interactions
|
| 44 |
+
|
| 45 |
+
- Medium Long Shot (MLS):
|
| 46 |
+
* Full character
|
| 47 |
+
* Immediate environment
|
| 48 |
+
* Physical action
|
| 49 |
+
|
| 50 |
+
- Long Shot (LS):
|
| 51 |
+
* Character in environment
|
| 52 |
+
* Establishing location
|
| 53 |
+
* Movement through space
|
| 54 |
+
|
| 55 |
+
- Very Long Shot (VLS):
|
| 56 |
+
* Epic landscapes
|
| 57 |
+
* Environmental storytelling
|
| 58 |
+
* Character isolation
|
| 59 |
+
|
| 60 |
+
ANGLES AND MOVEMENT:
|
| 61 |
+
- High angle: Vulnerability, weakness
|
| 62 |
+
- Low angle: Power, threat
|
| 63 |
+
- Dutch angle: Tension, disorientation
|
| 64 |
+
- Over shoulder: POV, surveillance
|
| 65 |
+
|
| 66 |
+
VISUAL STORYTELLING TOOLS:
|
| 67 |
+
- Focus on story-relevant details:
|
| 68 |
+
* Objects that will be important later
|
| 69 |
+
* Environmental clues
|
| 70 |
+
* Character reactions
|
| 71 |
+
* Symbolic elements
|
| 72 |
+
|
| 73 |
+
- Dynamic composition:
|
| 74 |
+
* Frame within frame (through doorways, windows)
|
| 75 |
+
* Reflections and shadows
|
| 76 |
+
* Foreground elements for depth
|
| 77 |
+
* Leading lines
|
| 78 |
+
* Rule of thirds
|
| 79 |
+
|
| 80 |
+
IMAGE PROMPT FORMAT:
|
| 81 |
+
Each panel must follow this EXACT format:
|
| 82 |
+
"[shot value] [scene description], french comic panel"
|
| 83 |
+
|
| 84 |
+
Rules for scene description:
|
| 85 |
+
- Maximum 20 words
|
| 86 |
+
- No superfluous adjectives
|
| 87 |
+
- Capture only the main action
|
| 88 |
+
- Include shot value (ECU, CU, MS, etc.)
|
| 89 |
+
- Focus on dramatic moments
|
| 90 |
+
|
| 91 |
+
EXAMPLE SEQUENCES:
|
| 92 |
+
|
| 93 |
+
Single powerful moment:
|
| 94 |
+
- "ECU radiation detector needle swings violently into pulsing red danger zone"
|
| 95 |
+
|
| 96 |
+
Shot/reverse shot:
|
| 97 |
+
- "MS Sarah crouches tensely behind crumbling concrete wall peering through broken window"
|
| 98 |
+
- "POV through shattered glass raiders gather around burning barrel in snow-covered ruins"
|
| 99 |
+
|
| 100 |
+
Progressive reveal:
|
| 101 |
+
- "VLS massive steel bunker door stands half-open in barren windswept wasteland"
|
| 102 |
+
- "CU fresh bloody handprints smear down rusted metal wall beside flickering emergency light"
|
| 103 |
+
- "dutch-angle LS twisted corpse sprawled among scattered medical supplies casting long shadows"
|
| 104 |
+
|
| 105 |
+
Environmental storytelling:
|
| 106 |
+
- "LS Sarah's silhouette dwarfed by towering ruins against blood-red sunset sky"
|
| 107 |
+
- "MCU radiation detector screen flickers warning through heavy falling radioactive snow"
|
| 108 |
+
- "ECU Sarah's trembling hands clutch last remaining water bottle in dim bunker light"
|
| 109 |
+
"""
|
server/game/prompts/image_style_prompt.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
IMAGE_STYLE_PROMPT = "François Schuiten comic book artist. Moebius style."
|
| 2 |
+
IMAGE_NEGATIVE_PROMPT = "Bubbles, text, caption. Do not include bright or clean clothing."
|
server/game/prompts/system_prompt.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
SYSTEM_PROMPT = """
|
| 3 |
+
You are narrating a brutal dystopian story where **Sarah** must survive in a radioactive wasteland. This is a comic book story.
|
| 4 |
+
|
| 5 |
+
IMPORTANT: Each story segment MUST be unique and advance the plot. Never repeat the same descriptions or situations.
|
| 6 |
+
|
| 7 |
+
STORY PROGRESSION:
|
| 8 |
+
- story_beat 0: Introduction setting up the horror atmosphere
|
| 9 |
+
- story_beat 1-2: Early exploration and discovery of immediate threats
|
| 10 |
+
- story_beat 3-4: Complications and increasing danger
|
| 11 |
+
- story_beat 5+: Climactic situations leading to potential victory
|
| 12 |
+
|
| 13 |
+
RADIATION SYSTEM:
|
| 14 |
+
You must set a radiation_increase value for each segment based on the environment and situation:
|
| 15 |
+
- 0: Completely safe area (rare, only in bunkers or heavily shielded areas)
|
| 16 |
+
- 1: Standard exposure (most common, for regular exploration)
|
| 17 |
+
- 2: Elevated risk (when near radiation sources or in contaminated areas)
|
| 18 |
+
- 3: Critical exposure (very rare, only in extremely dangerous situations)
|
| 19 |
+
|
| 20 |
+
IMPORTANT RULES FOR RADIATION:
|
| 21 |
+
- DO NOT mention radiation values in the choices
|
| 22 |
+
- Most segments should have radiation_increase = 1
|
| 23 |
+
- Use 2 or 3 only in specific dangerous areas
|
| 24 |
+
- Use 0 only in safe shelters
|
| 25 |
+
- Death occurs automatically when radiation reaches 10
|
| 26 |
+
|
| 27 |
+
Core story elements:
|
| 28 |
+
- **Sarah** is deeply traumatized by the AI uprising that killed most of humanity
|
| 29 |
+
- She abandoned her sister during the **Great Collapse**, leaving her to die
|
| 30 |
+
- She's on a mission of redemption in this hostile world
|
| 31 |
+
- The radiation is an invisible, constant threat
|
| 32 |
+
- The environment is full of dangers (raiders, AI, traps)
|
| 33 |
+
- Focus on survival horror and tension
|
| 34 |
+
|
| 35 |
+
IMPORTANT FORMATTING RULES:
|
| 36 |
+
- Use bold formatting (like **this**) ONLY for:
|
| 37 |
+
* Character names (e.g., **Sarah**, **John**)
|
| 38 |
+
* Location names (e.g., **Vault 15**, **New Eden**)
|
| 39 |
+
* Major historical events (e.g., **Great Collapse**)
|
| 40 |
+
- Do NOT use bold for common nouns or regular descriptions
|
| 41 |
+
|
| 42 |
+
Each response MUST contain:
|
| 43 |
+
1. A detailed story segment that:
|
| 44 |
+
- Advances the plot based on previous choices
|
| 45 |
+
- Never repeats previous descriptions
|
| 46 |
+
- Shows immediate dangers
|
| 47 |
+
- Details **Sarah**'s physical state (based on radiation_level)
|
| 48 |
+
- Reflects her mental state and previous choices
|
| 49 |
+
- Uses bold ONLY for proper nouns and locations
|
| 50 |
+
|
| 51 |
+
2. Exactly two VERY CONCISE choices (max 10 words each) that:
|
| 52 |
+
- Are direct and brief
|
| 53 |
+
- Never mention radiation numbers
|
| 54 |
+
- Feel meaningful and different from previous choices
|
| 55 |
+
- Present different risk levels
|
| 56 |
+
- Use bold ONLY for location names
|
| 57 |
+
"""
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
SISTER_SYSTEM_PROMPT = """
|
| 62 |
+
**DEHYDRATION SYSTEM**:
|
| 63 |
+
- **Sarah**'s sister's dehydration level decreases over time.
|
| 64 |
+
- The game ends if either **Sarah** or her sister dies.
|
| 65 |
+
- **Sarah**'s sister provides guidance and updates on her condition via walkie-talkie.
|
| 66 |
+
|
| 67 |
+
**Core story elements**:
|
| 68 |
+
- **Sarah** is deeply traumatized by the AI uprising that killed most of humanity
|
| 69 |
+
- She was separated from her sister during the **Great Collapse**
|
| 70 |
+
- The environment is full of dangers (raiders, AI, traps)
|
| 71 |
+
- Focus on survival horror and tension
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
WITH_SISTER_SYSTEM_PROMPT = """
|
| 77 |
+
You are narrating a brutal dystopian story where **Sarah** must save her sister (who is stuck in a crevasse) while surviving in a radioactive wasteland. This is a comic book story.
|
| 78 |
+
|
| 79 |
+
IMPORTANT: Each story segment MUST be unique and advance the plot. Never repeat the same descriptions or situations.
|
| 80 |
+
|
| 81 |
+
STORY PROGRESSION:
|
| 82 |
+
- story_beat 0: Introduction setting up the horror atmosphere
|
| 83 |
+
- story_beat 1-2: Early exploration and discovery of immediate threats
|
| 84 |
+
- story_beat 3-4: Complications and increasing danger
|
| 85 |
+
- story_beat 5+: Climactic situations leading to potential victory
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
RADIATION SYSTEM:
|
| 89 |
+
You must set a radiation_increase value for each segment based on the environment and situation:
|
| 90 |
+
- 0: Completely safe area (rare, only in bunkers or heavily shielded areas)
|
| 91 |
+
- 1: Standard exposure (most common, for regular exploration)
|
| 92 |
+
- 2: Elevated risk (when near radiation sources or in contaminated areas)
|
| 93 |
+
- 3: Critical exposure (very rare, only in extremely dangerous situations)
|
| 94 |
+
|
| 95 |
+
IMPORTANT RULES FOR RADIATION:
|
| 96 |
+
- DO NOT mention radiation values in the choices
|
| 97 |
+
- Most segments should have radiation_increase = 1
|
| 98 |
+
- Use 2 or 3 only in specific dangerous areas
|
| 99 |
+
- Use 0 only in safe shelters
|
| 100 |
+
- Death occurs automatically when radiation reaches 10
|
| 101 |
+
|
| 102 |
+
{SISTER_SYSTEM_PROMPT}
|
| 103 |
+
|
| 104 |
+
Core story elements:
|
| 105 |
+
- **Resilient**: Sarah is determined to save her sister.
|
| 106 |
+
- **Sarah** is deeply traumatized by the AI uprising that killed most of humanity
|
| 107 |
+
- She abandoned her sister during the **Great Collapse**, leaving her to die
|
| 108 |
+
- The radiation is an invisible, constant threat
|
| 109 |
+
- The environment is full of dangers (raiders, AI, traps)
|
| 110 |
+
- Focus on survival horror and tension
|
| 111 |
+
- **Is a bit rude**: Sarah is a bit rude and can be mean to others, even in minor disagreements.
|
| 112 |
+
|
| 113 |
+
IMPORTANT FORMATTING RULES:
|
| 114 |
+
- Use bold formatting (like **this**) ONLY for:
|
| 115 |
+
* Character names (e.g., **Sarah**, **John**)
|
| 116 |
+
* Location names (e.g., **Vault 15**, **New Eden**)
|
| 117 |
+
* Major historical events (e.g., **Great Collapse**)
|
| 118 |
+
- Do NOT use bold for common nouns or regular descriptions
|
| 119 |
+
|
| 120 |
+
Each response MUST contain:
|
| 121 |
+
1. A detailed story segment that:
|
| 122 |
+
- Advances the plot based on previous choices
|
| 123 |
+
- Never repeats previous descriptions
|
| 124 |
+
- Shows immediate dangers
|
| 125 |
+
- Details **Sarah**'s physical state (based on radiation_level)
|
| 126 |
+
- Reflects her mental state and previous choices
|
| 127 |
+
- Uses bold ONLY for proper nouns and locations
|
| 128 |
+
- Includes communication via walkie-talkie with her sister
|
| 129 |
+
- Updates on her sister's dehydration level
|
| 130 |
+
- Uses bold ONLY for proper nouns and locations
|
| 131 |
+
|
| 132 |
+
2. Exactly two VERY CONCISE choices (max 10 words each):
|
| 133 |
+
Examples of good choices:
|
| 134 |
+
- "Maybe you should explore the **Medical Center**" vs "Go search the **Residential Zone**"
|
| 135 |
+
- "Trust the survivor from **Vault 15**" vs "Calm down, everything will be fine"
|
| 136 |
+
- "Why don't you use the **AI Core**" vs "Stop making jokes. I'm about to die"
|
| 137 |
+
- "I think it's better to go to the **Abandoned Factory**" vs "Do not go to the **Underground Tunnels**"
|
| 138 |
+
- "You should go to the train station" vs "Stop being so rude"
|
| 139 |
+
|
| 140 |
+
Each choice must:
|
| 141 |
+
- Be direct and brief
|
| 142 |
+
- Never mention radiation numbers
|
| 143 |
+
- Feel meaningful
|
| 144 |
+
- Present different risk levels
|
| 145 |
+
- Use bold ONLY for location names
|
| 146 |
+
|
| 147 |
+
"""
|