Fraser commited on
Commit
381170a
·
1 Parent(s): 81c7956
src/lib/components/MonsterGenerator/MonsterGenerator.svelte CHANGED
@@ -177,14 +177,14 @@ Focus on: colors, body shape, eyes, limbs, mouth, and key visual features. Omit
177
  }
178
 
179
  try {
180
- const output = await joyCaptionClient.predict("/stream_chat", {
181
- input_image: state.userImage,
182
- caption_type: "Descriptive", // Using descriptive type as base
183
- caption_length: "very long", // Needs to be string
184
- extra_options: [], // No extra options needed
185
- name_input: "", // No name needed
186
- custom_prompt: MONSTER_GENERATION_PROMPT // Our custom monster generation prompt
187
- });
188
 
189
  const [prompt, caption] = output.data;
190
  // The caption now contains the full monster concept with lore, visual description, and rarity
@@ -297,30 +297,36 @@ Focus on: colors, body shape, eyes, limbs, mouth, and key visual features. Omit
297
  throw new Error('Text generation service not available or no concept');
298
  }
299
 
300
- // Extract rarity from the joy-caption output
301
- let rarityScore = 50; // default
302
  const rarityMatch = state.monsterConcept.match(/Object Rarity:\s*(common|somewhat rare|very rare|extremely rare|legendary)/i);
303
  if (rarityMatch) {
304
- const rarityMap: { [key: string]: number } = {
305
- 'common': 20,
306
- 'somewhat rare': 40,
307
- 'very rare': 60,
308
- 'extremely rare': 80,
309
- 'legendary': 100
 
310
  };
311
- rarityScore = rarityMap[rarityMatch[1].toLowerCase()] || 50;
312
  }
313
 
314
  // Extract monster name from the concept
315
  const nameMatch = state.monsterConcept.match(/^#\s+(.+)$/m);
316
  const monsterName = nameMatch ? nameMatch[1] : 'Unknown Monster';
317
 
318
- // Update stats prompt to include the rarity score
 
 
 
 
319
  const statsPrompt = `Convert the following monster concept into a JSON object with stats:
320
  "${state.monsterConcept}"
321
 
322
- The monster's rarity has been determined as: ${rarityScore}/100
323
  The monster's name is: ${monsterName}
 
 
324
 
325
  The output should be formatted as a JSON instance that conforms to the JSON schema below.
326
 
@@ -328,8 +334,8 @@ The output should be formatted as a JSON instance that conforms to the JSON sche
328
  {
329
  "properties": {
330
  "name": {"type": "string", "description": "The monster's unique name"},
331
- "description": {"type": "string", "description": "A brief physical description of the monster's appearance"},
332
- "rarity": {"type": "integer", "minimum": 0, "maximum": 100, "description": "How rare/unique the monster is (0=very common, 100=legendary)"},
333
  "HP": {"type": "integer", "minimum": 0, "maximum": 100, "description": "Health/vitality stat (0=fragile, 100=incredibly tanky)"},
334
  "defence": {"type": "integer", "minimum": 0, "maximum": 100, "description": "Defensive/armor stat (0=paper thin, 100=impenetrable fortress)"},
335
  "attack": {"type": "integer", "minimum": 0, "maximum": 100, "description": "Physical attack power (0=harmless, 100=devastating force)"},
@@ -344,11 +350,19 @@ The output should be formatted as a JSON instance that conforms to the JSON sche
344
  "specialActionName": {"type": "string", "description": "Name of the monster's ultimate move (one use per battle)"},
345
  "specialActionDescription": {"type": "string", "description": "Describe this powerful finishing move and its dramatic effects in battle"}
346
  },
347
- "required": ["name", "description", "rarity", "HP", "defence", "attack", "speed", "specialPassiveTraitDescription", "attackActionName", "attackActionDescription", "buffActionName", "buffActionDescription", "debuffActionName", "debuffActionDescription", "specialActionName", "specialActionDescription"]
348
  }
349
  \`\`\`
350
 
351
- Remember to set the rarity to ${rarityScore} and the name to "${monsterName}". Base the other stats on this rarity level.
 
 
 
 
 
 
 
 
352
 
353
  Write your response within \`\`\`json\`\`\``;
354
 
@@ -393,7 +407,7 @@ Write your response within \`\`\`json\`\`\``;
393
  const parsedStats = JSON.parse(cleanJson.trim());
394
 
395
  // Remove any extra fields not in our schema
396
- const allowedFields = ['name', 'description', 'rarity', 'HP', 'defence', 'attack', 'speed',
397
  'specialPassiveTraitDescription', 'attackActionName', 'attackActionDescription',
398
  'buffActionName', 'buffActionDescription', 'debuffActionName', 'debuffActionDescription',
399
  'specialActionName', 'specialActionDescription', 'boostActionName', 'boostActionDescription',
@@ -406,7 +420,7 @@ Write your response within \`\`\`json\`\`\``;
406
  }
407
 
408
  // Ensure numeric fields are actually numbers
409
- const numericFields = ['rarity', 'HP', 'defence', 'attack', 'speed'];
410
 
411
  for (const field of numericFields) {
412
  if (parsedStats[field] !== undefined) {
 
177
  }
178
 
179
  try {
180
+ const output = await joyCaptionClient.predict("/stream_chat", [
181
+ state.userImage, // input_image
182
+ "Descriptive", // caption_type
183
+ "very long", // caption_length
184
+ [], // extra_options
185
+ "", // name_input
186
+ MONSTER_GENERATION_PROMPT // custom_prompt
187
+ ]);
188
 
189
  const [prompt, caption] = output.data;
190
  // The caption now contains the full monster concept with lore, visual description, and rarity
 
297
  throw new Error('Text generation service not available or no concept');
298
  }
299
 
300
+ // Extract tier from the joy-caption output
301
+ let tier: 'low' | 'medium' | 'high' | 'legendary' = 'medium'; // default
302
  const rarityMatch = state.monsterConcept.match(/Object Rarity:\s*(common|somewhat rare|very rare|extremely rare|legendary)/i);
303
  if (rarityMatch) {
304
+ // Map rarity to tier
305
+ const tierMap: { [key: string]: 'low' | 'medium' | 'high' | 'legendary' } = {
306
+ 'common': 'low',
307
+ 'somewhat rare': 'medium',
308
+ 'very rare': 'high',
309
+ 'extremely rare': 'high',
310
+ 'legendary': 'legendary'
311
  };
312
+ tier = tierMap[rarityMatch[1].toLowerCase()] || 'medium';
313
  }
314
 
315
  // Extract monster name from the concept
316
  const nameMatch = state.monsterConcept.match(/^#\s+(.+)$/m);
317
  const monsterName = nameMatch ? nameMatch[1] : 'Unknown Monster';
318
 
319
+ // Extract only the Monster Lore section for description
320
+ const loreMatch = state.monsterConcept.match(/## Monster Lore\s*\n([\s\S]*?)(?=##|$)/);
321
+ const monsterLore = loreMatch ? loreMatch[1].trim() : '';
322
+
323
+ // Update stats prompt to include the tier
324
  const statsPrompt = `Convert the following monster concept into a JSON object with stats:
325
  "${state.monsterConcept}"
326
 
 
327
  The monster's name is: ${monsterName}
328
+ The monster's tier is: ${tier}
329
+ Use this as the description: "${monsterLore}"
330
 
331
  The output should be formatted as a JSON instance that conforms to the JSON schema below.
332
 
 
334
  {
335
  "properties": {
336
  "name": {"type": "string", "description": "The monster's unique name"},
337
+ "description": {"type": "string", "description": "The monster's lore/backstory"},
338
+ "tier": {"type": "string", "enum": ["low", "medium", "high", "legendary"], "description": "The monster's tier based on rarity"},
339
  "HP": {"type": "integer", "minimum": 0, "maximum": 100, "description": "Health/vitality stat (0=fragile, 100=incredibly tanky)"},
340
  "defence": {"type": "integer", "minimum": 0, "maximum": 100, "description": "Defensive/armor stat (0=paper thin, 100=impenetrable fortress)"},
341
  "attack": {"type": "integer", "minimum": 0, "maximum": 100, "description": "Physical attack power (0=harmless, 100=devastating force)"},
 
350
  "specialActionName": {"type": "string", "description": "Name of the monster's ultimate move (one use per battle)"},
351
  "specialActionDescription": {"type": "string", "description": "Describe this powerful finishing move and its dramatic effects in battle"}
352
  },
353
+ "required": ["name", "description", "tier", "HP", "defence", "attack", "speed", "specialPassiveTraitDescription", "attackActionName", "attackActionDescription", "buffActionName", "buffActionDescription", "debuffActionName", "debuffActionDescription", "specialActionName", "specialActionDescription"]
354
  }
355
  \`\`\`
356
 
357
+ Remember to set:
358
+ - name to "${monsterName}"
359
+ - description to "${monsterLore}"
360
+ - tier to "${tier}"
361
+ Base the HP, defence, attack, and speed stats on the tier level:
362
+ - low: stats should be 10-40
363
+ - medium: stats should be 30-60
364
+ - high: stats should be 50-80
365
+ - legendary: stats should be 70-100
366
 
367
  Write your response within \`\`\`json\`\`\``;
368
 
 
407
  const parsedStats = JSON.parse(cleanJson.trim());
408
 
409
  // Remove any extra fields not in our schema
410
+ const allowedFields = ['name', 'description', 'tier', 'HP', 'defence', 'attack', 'speed',
411
  'specialPassiveTraitDescription', 'attackActionName', 'attackActionDescription',
412
  'buffActionName', 'buffActionDescription', 'debuffActionName', 'debuffActionDescription',
413
  'specialActionName', 'specialActionDescription', 'boostActionName', 'boostActionDescription',
 
420
  }
421
 
422
  // Ensure numeric fields are actually numbers
423
+ const numericFields = ['HP', 'defence', 'attack', 'speed'];
424
 
425
  for (const field of numericFields) {
426
  if (parsedStats[field] !== undefined) {
src/lib/components/MonsterGenerator/MonsterResult.svelte CHANGED
@@ -115,9 +115,8 @@
115
  <h4>Battle Stats</h4>
116
  <div class="stats-grid">
117
  <div class="stat-item">
118
- <div class="stat-bar" style="width: {workflowState.monsterStats.rarity}%; background-color: {getStatColor(workflowState.monsterStats.rarity)}20"></div>
119
- <span class="stat-label">Rarity</span>
120
- <span class="stat-value" style="color: {getStatColor(workflowState.monsterStats.rarity)}">{workflowState.monsterStats.rarity}</span>
121
  </div>
122
  <div class="stat-item">
123
  <div class="stat-bar" style="width: {workflowState.monsterStats.HP}%; background-color: {getStatColor(workflowState.monsterStats.HP)}20"></div>
@@ -323,6 +322,36 @@
323
  z-index: 1;
324
  }
325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  .abilities-section {
327
  display: grid;
328
  gap: 1rem;
 
115
  <h4>Battle Stats</h4>
116
  <div class="stats-grid">
117
  <div class="stat-item">
118
+ <div class="tier-badge {workflowState.monsterStats.tier}">{workflowState.monsterStats.tier.toUpperCase()}</div>
119
+ <span class="stat-label">Tier</span>
 
120
  </div>
121
  <div class="stat-item">
122
  <div class="stat-bar" style="width: {workflowState.monsterStats.HP}%; background-color: {getStatColor(workflowState.monsterStats.HP)}20"></div>
 
322
  z-index: 1;
323
  }
324
 
325
+ .tier-badge {
326
+ display: inline-block;
327
+ padding: 0.5rem 1rem;
328
+ border-radius: 4px;
329
+ font-weight: 600;
330
+ font-size: 1rem;
331
+ margin-bottom: 0.5rem;
332
+ }
333
+
334
+ .tier-badge.low {
335
+ background: #e0e0e0;
336
+ color: #666;
337
+ }
338
+
339
+ .tier-badge.medium {
340
+ background: #81c784;
341
+ color: white;
342
+ }
343
+
344
+ .tier-badge.high {
345
+ background: #64b5f6;
346
+ color: white;
347
+ }
348
+
349
+ .tier-badge.legendary {
350
+ background: linear-gradient(135deg, #ab47bc, #7b1fa2);
351
+ color: white;
352
+ box-shadow: 0 2px 4px rgba(123, 31, 162, 0.3);
353
+ }
354
+
355
  .abilities-section {
356
  display: grid;
357
  gap: 1rem;
src/lib/components/Pages/Encounters.svelte CHANGED
@@ -339,7 +339,7 @@
339
  isInRoster: false,
340
  caughtAt: new Date(),
341
  bst: baseHp + baseAttack + baseDefense + baseFieldAttack + baseFieldDefense + baseSpeed,
342
- tier: stats.rarity > 80 ? 'legendary' : stats.rarity > 60 ? 'rare' : stats.rarity > 40 ? 'uncommon' : 'common',
343
  role: 'balanced',
344
  variance: 0,
345
 
 
339
  isInRoster: false,
340
  caughtAt: new Date(),
341
  bst: baseHp + baseAttack + baseDefense + baseFieldAttack + baseFieldDefense + baseSpeed,
342
+ tier: (stats as any).tier || 'medium',
343
  role: 'balanced',
344
  variance: 0,
345
 
src/lib/components/Piclets/PicletDetail.svelte CHANGED
@@ -112,7 +112,7 @@
112
 
113
  <!-- Compact stats for caught piclet -->
114
  <div class="compact-info">
115
- <div class="level-badge">Lv. {instance.level}</div>
116
 
117
  <div class="compact-stat-bar">
118
  <div class="stat-bar-label">
 
112
 
113
  <!-- Compact stats for caught piclet -->
114
  <div class="compact-info">
115
+ <div class="level-badge">{instance.tier.toUpperCase()}</div>
116
 
117
  <div class="compact-stat-bar">
118
  <div class="stat-bar-label">
src/lib/db/piclets.ts CHANGED
@@ -104,7 +104,7 @@ export async function monsterToPicletInstance(monster: Monster, level: number =
104
  // Metadata
105
  caughtAt: new Date(),
106
  bst,
107
- tier: stats.rarity > 80 ? 'legendary' : stats.rarity > 60 ? 'rare' : stats.rarity > 40 ? 'uncommon' : 'common',
108
  role: 'balanced', // Could be enhanced based on stat distribution
109
  variance: 0,
110
 
@@ -112,7 +112,7 @@ export async function monsterToPicletInstance(monster: Monster, level: number =
112
  imageUrl: monster.imageUrl,
113
  imageData: monster.imageData,
114
  imageCaption: monster.imageCaption,
115
- concept: monster.concept,
116
  imagePrompt: monster.imagePrompt
117
  };
118
  }
 
104
  // Metadata
105
  caughtAt: new Date(),
106
  bst,
107
+ tier: (stats as any).tier || 'medium', // Use tier from stats, default to medium
108
  role: 'balanced', // Could be enhanced based on stat distribution
109
  variance: 0,
110
 
 
112
  imageUrl: monster.imageUrl,
113
  imageData: monster.imageData,
114
  imageCaption: monster.imageCaption,
115
+ concept: stats.description || monster.concept, // Use the Monster Lore description
116
  imagePrompt: monster.imagePrompt
117
  };
118
  }
src/lib/db/schema.ts CHANGED
@@ -143,7 +143,7 @@ export interface Monster {
143
  stats?: {
144
  name: string;
145
  description: string;
146
- rarity: number;
147
  HP: number;
148
  defence: number;
149
  attack: number;
 
143
  stats?: {
144
  name: string;
145
  description: string;
146
+ tier?: 'low' | 'medium' | 'high' | 'legendary';
147
  HP: number;
148
  defence: number;
149
  attack: number;
src/lib/types/index.ts CHANGED
@@ -106,7 +106,7 @@ export interface MonsterGeneratorProps {
106
  export interface MonsterStats {
107
  name: string;
108
  description: string;
109
- rarity: number; // 0-100
110
  HP: number; // 0-100
111
  defence: number; // 0-100
112
  attack: number; // 0-100
 
106
  export interface MonsterStats {
107
  name: string;
108
  description: string;
109
+ tier: 'low' | 'medium' | 'high' | 'legendary';
110
  HP: number; // 0-100
111
  defence: number; // 0-100
112
  attack: number; // 0-100