piclets / src /lib /db /piclets.ts
Fraser's picture
monster-piclet
55b1a24
raw
history blame
7.06 kB
import { db } from './index';
import type { PicletInstance, Monster, BattleMove } from './schema';
import { PicletType, AttackType, getTypeFromConcept } from '../types/picletTypes';
// Convert a generated Monster to a PicletInstance
export async function monsterToPicletInstance(monster: Monster, level: number = 5): Promise<Omit<PicletInstance, 'id'>> {
if (!monster.stats) {
throw new Error('Monster must have stats to create PicletInstance');
}
const stats = monster.stats;
// Calculate base stats from the 0-100 scale
const baseHp = Math.floor(stats.HP * 2 + 50);
const baseAttack = Math.floor(stats.attack * 1.5 + 30);
const baseDefense = Math.floor(stats.defence * 1.5 + 30);
const baseSpeed = Math.floor(stats.speed * 1.5 + 30);
// Field stats are variations of regular stats
const baseFieldAttack = Math.floor(baseAttack * 0.8);
const baseFieldDefense = Math.floor(baseDefense * 0.8);
// Calculate current stats based on level
const calculateStat = (base: number, level: number) => Math.floor((base * level) / 50 + 5);
const calculateHp = (base: number, level: number) => Math.floor((base * level) / 50 + level + 10);
const maxHp = calculateHp(baseHp, level);
// Determine primary type - use picletType from stats if available, otherwise auto-detect
let primaryType: PicletType;
if (monster.stats && (monster.stats as any).picletType) {
// Use the type determined during stat generation, but validate it
const statsType = (monster.stats as any).picletType as string;
const normalizedType = statsType.toLowerCase();
// Validate that the type exists in our enum
const validType = Object.values(PicletType).find(type => type === normalizedType);
if (validType) {
primaryType = validType;
} else {
console.warn(`Invalid picletType "${statsType}" from stats, falling back to concept detection`);
primaryType = getTypeFromConcept(monster.concept, monster.imageCaption);
}
} else {
// Fallback to concept-based detection
primaryType = getTypeFromConcept(monster.concept, monster.imageCaption);
}
// Create moves based on monster's abilities
const moves: BattleMove[] = [
{
name: stats.attackActionName,
type: primaryType as unknown as AttackType,
power: 50,
accuracy: 95,
pp: 20,
currentPp: 20,
description: stats.attackActionDescription
},
{
name: stats.buffActionName,
type: AttackType.NORMAL,
power: 0,
accuracy: 100,
pp: 15,
currentPp: 15,
description: stats.buffActionDescription
},
{
name: stats.debuffActionName,
type: AttackType.NORMAL,
power: 0,
accuracy: 85,
pp: 15,
currentPp: 15,
description: stats.debuffActionDescription
},
{
name: stats.specialActionName,
type: primaryType as unknown as AttackType,
power: 80,
accuracy: 90,
pp: 5,
currentPp: 5,
description: stats.specialActionDescription
}
];
const bst = baseHp + baseAttack + baseDefense + baseFieldAttack + baseFieldDefense + baseSpeed;
return {
// Type Info
typeId: monster.name.toLowerCase().replace(/\s+/g, '-'),
nickname: monster.name,
primaryType: primaryType,
secondaryType: undefined,
// Current Stats
currentHp: maxHp,
maxHp,
level,
xp: 0,
attack: calculateStat(baseAttack, level),
defense: calculateStat(baseDefense, level),
fieldAttack: calculateStat(baseFieldAttack, level),
fieldDefense: calculateStat(baseFieldDefense, level),
speed: calculateStat(baseSpeed, level),
// Base Stats
baseHp,
baseAttack,
baseDefense,
baseFieldAttack,
baseFieldDefense,
baseSpeed,
// Battle
moves,
nature: 'hardy', // Default nature
// Roster
isInRoster: false,
rosterPosition: undefined,
// Metadata
caughtAt: new Date(),
bst,
tier: (stats as any).tier || 'medium', // Use tier from stats, default to medium
role: 'balanced', // Could be enhanced based on stat distribution
variance: 0,
// Original generation data
imageUrl: monster.imageUrl,
imageData: monster.imageData,
imageCaption: monster.imageCaption,
concept: stats.description || monster.concept, // Use the Monster Lore description
imagePrompt: monster.imagePrompt
};
}
// Save a new PicletInstance
export async function savePicletInstance(piclet: Omit<PicletInstance, 'id'>): Promise<number> {
return await db.picletInstances.add(piclet);
}
// Get all PicletInstances
export async function getAllPicletInstances(): Promise<PicletInstance[]> {
return await db.picletInstances.toArray();
}
// Get roster PicletInstances
export async function getRosterPiclets(): Promise<PicletInstance[]> {
const allPiclets = await db.picletInstances.toArray();
return allPiclets
.filter(p =>
p.rosterPosition !== undefined &&
p.rosterPosition !== null &&
p.rosterPosition >= 0 &&
p.rosterPosition <= 5
)
.sort((a, b) => (a.rosterPosition ?? 0) - (b.rosterPosition ?? 0));
}
// Update roster position
export async function updateRosterPosition(id: number, position: number | undefined): Promise<void> {
await db.picletInstances.update(id, {
isInRoster: position !== undefined,
rosterPosition: position
});
}
// Move piclet to roster
export async function moveToRoster(id: number, position: number): Promise<void> {
// Check if position is already occupied
const existingPiclet = await db.picletInstances
.where('rosterPosition')
.equals(position)
.and(item => item.isInRoster)
.first();
if (existingPiclet) {
// Move existing piclet to storage
await db.picletInstances.update(existingPiclet.id!, {
isInRoster: false,
rosterPosition: undefined
});
}
// Move new piclet to roster
await db.picletInstances.update(id, {
isInRoster: true,
rosterPosition: position
});
}
// Swap roster positions
export async function swapRosterPositions(id1: number, position1: number, id2: number, position2: number): Promise<void> {
await db.transaction('rw', db.picletInstances, async () => {
await db.picletInstances.update(id1, { rosterPosition: position2 });
await db.picletInstances.update(id2, { rosterPosition: position1 });
});
}
// Move piclet to storage
export async function moveToStorage(id: number): Promise<void> {
await db.picletInstances.update(id, {
isInRoster: false,
rosterPosition: undefined
});
}
// Get storage piclets
export async function getStoragePiclets(): Promise<PicletInstance[]> {
const allPiclets = await db.picletInstances.toArray();
return allPiclets.filter(p =>
p.rosterPosition === undefined ||
p.rosterPosition === null ||
p.rosterPosition < 0 ||
p.rosterPosition > 5
);
}
// Delete a PicletInstance
export async function deletePicletInstance(id: number): Promise<void> {
await db.picletInstances.delete(id);
}