Justxd22 commited on
Commit
6f9a920
·
1 Parent(s): 1bf0383

Add suspect profile image prompts and metadata for rendering

Browse files
TODO.md CHANGED
@@ -5,13 +5,15 @@
5
  - players can't know there's more suspects down the list as we removed the scroll bar, can you scroll a bit down by default to show there's more cards down
6
  - find the texture missing
7
  - typing effect
 
8
 
9
 
10
  # Doing
 
 
11
  - mode selection
12
  - numbered steps to launch game
13
 
14
- - pre-generate suspects cartoony potraits
15
  - sound/music
16
  - eleven labs tts/voice
17
  - video embeded
 
5
  - players can't know there's more suspects down the list as we removed the scroll bar, can you scroll a bit down by default to show there's more cards down
6
  - find the texture missing
7
  - typing effect
8
+ - pre-generate suspects cartoony potraits
9
 
10
 
11
  # Doing
12
+ - bottom gradio box to see internal MCP tools calls out/in
13
+ - add ai
14
  - mode selection
15
  - numbered steps to launch game
16
 
 
17
  - sound/music
18
  - eleven labs tts/voice
19
  - video embeded
prompts/image_generation.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Suspect Profile Image Prompts
2
+ ## Model: Nano Banana (or standard SDXL)
3
+ ## Style: Noir Graphic Novel, Digital Art, Semi-Realistic Game Asset
4
+ ## Note: Square aspect ratio (1:1). NO polaroid frames in the image itself.
5
+
6
+ Use these prompts to generate profile images. Replace `{gender}` with "man" or "woman".
7
+
8
+ ### 1. Executive (Archetype: "executive")
9
+ > **Prompt:**
10
+ > close up portrait of a {gender} corporate executive, sharp business suit, looking tired and stressed, office background, noir graphic novel style, digital painting, high contrast, dramatic lighting, detailed face, 8k, no frame
11
+
12
+ ### 2. Worker (Archetype: "worker")
13
+ > **Prompt:**
14
+ > close up portrait of a {gender} blue collar worker, janitor uniform or apron, rough skin texture, suspicious eyes, dark alley or kitchen background, noir style, gritty, sepia tone, shadow over face, digital art, game asset, no frame
15
+
16
+ ### 3. Socialite (Archetype: "socialite")
17
+ > **Prompt:**
18
+ > close up portrait of a wealthy {gender} socialite, elegant evening wear, pearl necklace or tuxedo, arrogant expression, luxury interior background, noir style, dramatic shadows, vintage aesthetic, digital painting, 8k, no frame
19
+
20
+ ### 4. Artist (Archetype: "artist")
21
+ > **Prompt:**
22
+ > close up portrait of a {gender} artist, bohemian style, messy hair, scarf, intense stare, art studio background, noir graphic novel style, high contrast, black and white with red accent, digital art, no frame
23
+
24
+ ### 5. Criminal / Suspect (Archetype: "criminal")
25
+ > **Prompt:**
26
+ > close up portrait of a {gender} suspect, hoodie or leather jacket, face partially shadowed, street background, noir detective style, gritty, mystery game character, digital painting, 8k, no frame
27
+
28
+ ### 6. Detective / Private Eye (Archetype: "detective")
29
+ > **Prompt:**
30
+ > close up portrait of a {gender} detective, trench coat, fedora, rainy background, noir style, gritty, cynical expression, digital art, game asset, high contrast, no frame
31
+
32
+ ---
33
+ **Instructions:**
34
+ 1. Generate 2-3 variations for each archetype (Male and Female).
35
+ 2. Save as `suspect_1.jpg`, `suspect_2.jpg`, etc.
36
+ 3. Update `metadata.json` with the filename, gender, and archetype.
ui/static/assets/suspects/metadata.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {"id": "suspect_10.png", "gender": "male", "archetype": "detective"},
3
+ {"id": "suspect_11.png", "gender": "female", "archetype": "socialite"},
4
+ {"id": "suspect_9.png", "gender": "male", "archetype": "socialite"},
5
+ {"id": "suspect_3.png", "gender": "female", "archetype": "executive"},
6
+ {"id": "suspect_5.png", "gender": "male", "archetype": "executive"},
7
+ {"id": "suspect_4.png", "gender": "female", "archetype": "artist"},
8
+ {"id": "suspect_1.png", "gender": "male", "archetype": "artist"},
9
+ {"id": "suspect_7.png", "gender": "male", "archetype": "worker"},
10
+ {"id": "suspect_2.png", "gender": "female", "archetype": "worker"},
11
+ {"id": "suspect_6.png", "gender": "male", "archetype": "criminal"},
12
+ {"id": "suspect_8.png", "gender": "female", "archetype": "criminal"},
13
+ {"id": "suspect_12.png", "gender": "female", "archetype": "customer"},
14
+ {"id": "suspect_13.png", "gender": "male", "archetype": "customer"}
15
+ ]
ui/static/js/game_logic.js CHANGED
@@ -11,7 +11,8 @@ let gameState = {
11
  nextEvidenceSlot: 0, // Track grid position for new cards
12
  availableCameras: [],
13
  dnaMap: {},
14
- unlockedEvidence: []
 
15
  };
16
 
17
  // --- Bridge: Communication with Parent (Python/Gradio) ---
@@ -114,7 +115,19 @@ function updateStatus(data) {
114
 
115
  function initializeGame(data) {
116
  gameState = data;
117
- renderSuspects();
 
 
 
 
 
 
 
 
 
 
 
 
118
  document.getElementById('loading-overlay').style.opacity = 0;
119
  setTimeout(() => {
120
  document.getElementById('loading-overlay').style.display = 'none';
@@ -244,7 +257,7 @@ function renderSuspects() {
244
 
245
  if (!gameState.scenario || !gameState.scenario.suspects) return;
246
 
247
- gameState.scenario.suspects.forEach(suspect => {
248
  const card = document.createElement('div');
249
  card.className = 'suspect-card';
250
  card.dataset.id = suspect.id;
@@ -253,8 +266,47 @@ function renderSuspects() {
253
  // Placeholder Avatar (Initials)
254
  const initials = suspect.name.split(' ').map(n => n[0]).join('');
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  card.innerHTML = `
257
- <div class="suspect-img">${initials}</div>
 
 
 
 
258
  <div class="suspect-name">${suspect.name}</div>
259
  <div class="suspect-role">${suspect.role}</div>
260
  <div class="suspect-phone" title="Click to copy" onclick="event.stopPropagation(); copyToClipboard('${suspect.phone_number}')">
 
11
  nextEvidenceSlot: 0, // Track grid position for new cards
12
  availableCameras: [],
13
  dnaMap: {},
14
+ unlockedEvidence: [],
15
+ imageMetadata: []
16
  };
17
 
18
  // --- Bridge: Communication with Parent (Python/Gradio) ---
 
115
 
116
  function initializeGame(data) {
117
  gameState = data;
118
+
119
+ // Fetch image metadata then render
120
+ fetch('/static/assets/suspects/metadata.json')
121
+ .then(res => res.json())
122
+ .then(metadata => {
123
+ gameState.imageMetadata = metadata;
124
+ renderSuspects();
125
+ })
126
+ .catch(err => {
127
+ console.error("Failed to load image metadata:", err);
128
+ renderSuspects(); // Fallback
129
+ });
130
+
131
  document.getElementById('loading-overlay').style.opacity = 0;
132
  setTimeout(() => {
133
  document.getElementById('loading-overlay').style.display = 'none';
 
257
 
258
  if (!gameState.scenario || !gameState.scenario.suspects) return;
259
 
260
+ gameState.scenario.suspects.forEach((suspect, index) => {
261
  const card = document.createElement('div');
262
  card.className = 'suspect-card';
263
  card.dataset.id = suspect.id;
 
266
  // Placeholder Avatar (Initials)
267
  const initials = suspect.name.split(' ').map(n => n[0]).join('');
268
 
269
+ // Select Image based on Gender & Archetype
270
+ let imgFilename = `suspect_${(index % 8) + 1}.jpg`; // Default fallback
271
+
272
+ if (gameState.imageMetadata && gameState.imageMetadata.length > 0 && suspect.gender) {
273
+ // 1. Filter by Gender
274
+ const genderMatches = gameState.imageMetadata.filter(img => img.gender === suspect.gender.toLowerCase());
275
+
276
+ if (genderMatches.length > 0) {
277
+ // 2. Try to match Archetype
278
+ const role = suspect.role.toLowerCase();
279
+ let targetArchetype = "detective"; // default
280
+
281
+ if (role.includes("ceo") || role.includes("cfo") || role.includes("manager") || role.includes("dealer")) targetArchetype = "executive";
282
+ else if (role.includes("janitor") || role.includes("chef") || role.includes("caterer")) targetArchetype = "worker";
283
+ else if (role.includes("artist") || role.includes("curator")) targetArchetype = "artist";
284
+ else if (role.includes("heir") || role.includes("collector") || role.includes("sister") || role.includes("socialite")) targetArchetype = "socialite";
285
+ else if (role.includes("ex-")) targetArchetype = "criminal";
286
+ else if (role.includes("customer")) targetArchetype = "customer"; // Rough/casual look
287
+
288
+ const archetypeMatches = genderMatches.filter(img => img.archetype === targetArchetype);
289
+
290
+ // Use archetype matches if available, otherwise use all gender matches
291
+ const pool = archetypeMatches.length > 0 ? archetypeMatches : genderMatches;
292
+
293
+ // Deterministic pick based on name
294
+ let hash = 0;
295
+ for (let i = 0; i < suspect.name.length; i++) {
296
+ hash = ((hash << 5) - hash) + suspect.name.charCodeAt(i);
297
+ hash |= 0;
298
+ }
299
+ const selected = pool[Math.abs(hash) % pool.length];
300
+ imgFilename = selected.id;
301
+ }
302
+ }
303
+
304
  card.innerHTML = `
305
+ <div class="suspect-img">
306
+ <img src="/static/assets/suspects/${imgFilename}"
307
+ style="width:100%; height:100%; object-fit:cover; display:block;"
308
+ onerror="this.style.display='none'; this.parentElement.innerText='${initials}'; this.parentElement.style.display='flex'; this.parentElement.style.alignItems='center'; this.parentElement.style.justifyContent='center';">
309
+ </div>
310
  <div class="suspect-name">${suspect.name}</div>
311
  <div class="suspect-role">${suspect.role}</div>
312
  <div class="suspect-phone" title="Click to copy" onclick="event.stopPropagation(); copyToClipboard('${suspect.phone_number}')">