Fraser commited on
Commit
b66ef35
·
1 Parent(s): e223b2b

more stats

Browse files
src/lib/db/gameState.ts ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { db } from './index';
2
+ import type { GameState, Encounter, EncounterType } from './schema';
3
+
4
+ // Initialize or get game state
5
+ export async function getOrCreateGameState(): Promise<GameState> {
6
+ const states = await db.gameState.toArray();
7
+
8
+ if (states.length > 0) {
9
+ // Update last played
10
+ await db.gameState.update(states[0].id!, { lastPlayed: new Date() });
11
+ return states[0];
12
+ }
13
+
14
+ // Create initial game state
15
+ const newState: Omit<GameState, 'id'> = {
16
+ lastEncounterRefresh: new Date(),
17
+ lastPlayed: new Date(),
18
+ progressPoints: 0,
19
+ trainersDefeated: 0,
20
+ picletsCapured: 0,
21
+ battlesLost: 0
22
+ };
23
+
24
+ const id = await db.gameState.add(newState);
25
+ return { ...newState, id };
26
+ }
27
+
28
+ // Update progress
29
+ export async function updateProgress(updates: Partial<GameState>): Promise<void> {
30
+ const state = await getOrCreateGameState();
31
+ await db.gameState.update(state.id!, updates);
32
+ }
33
+
34
+ // Increment specific counters
35
+ export async function incrementCounter(counter: 'trainersDefeated' | 'picletsCapured' | 'battlesLost'): Promise<void> {
36
+ const state = await getOrCreateGameState();
37
+ await db.gameState.update(state.id!, {
38
+ [counter]: state[counter] + 1
39
+ });
40
+ }
41
+
42
+ // Add progress points
43
+ export async function addProgressPoints(points: number): Promise<void> {
44
+ const state = await getOrCreateGameState();
45
+ const newPoints = Math.min(1000, state.progressPoints + points);
46
+ await db.gameState.update(state.id!, {
47
+ progressPoints: newPoints
48
+ });
49
+ }
50
+
51
+ // Create a new encounter
52
+ export async function createEncounter(encounter: Omit<Encounter, 'id' | 'createdAt'>): Promise<number> {
53
+ return await db.encounters.add({
54
+ ...encounter,
55
+ createdAt: new Date()
56
+ });
57
+ }
58
+
59
+ // Get recent encounters
60
+ export async function getRecentEncounters(limit: number = 10): Promise<Encounter[]> {
61
+ return await db.encounters
62
+ .orderBy('createdAt')
63
+ .reverse()
64
+ .limit(limit)
65
+ .toArray();
66
+ }
67
+
68
+ // Get encounters by type
69
+ export async function getEncountersByType(type: EncounterType): Promise<Encounter[]> {
70
+ return await db.encounters
71
+ .where('type')
72
+ .equals(type)
73
+ .toArray();
74
+ }
75
+
76
+ // Check if should refresh encounters (e.g., every hour)
77
+ export async function shouldRefreshEncounters(): Promise<boolean> {
78
+ const state = await getOrCreateGameState();
79
+ const hoursSinceRefresh = (Date.now() - state.lastEncounterRefresh.getTime()) / (1000 * 60 * 60);
80
+ return hoursSinceRefresh >= 1;
81
+ }
82
+
83
+ // Mark encounters as refreshed
84
+ export async function markEncountersRefreshed(): Promise<void> {
85
+ const state = await getOrCreateGameState();
86
+ await db.gameState.update(state.id!, {
87
+ lastEncounterRefresh: new Date()
88
+ });
89
+ }
src/lib/db/index.ts CHANGED
@@ -1,11 +1,17 @@
1
  import Dexie, { type Table } from 'dexie';
2
- import type { Monster } from './schema';
3
 
4
- export class MonsterDatabase extends Dexie {
 
5
  monsters!: Table<Monster>;
 
 
 
 
 
6
 
7
  constructor() {
8
- super('MonsterGeneratorDB');
9
 
10
  this.version(1).stores({
11
  monsters: '++id, name, createdAt'
@@ -30,7 +36,15 @@ export class MonsterDatabase extends Dexie {
30
  monster.stats = monster.stats || null;
31
  });
32
  });
 
 
 
 
 
 
 
 
33
  }
34
  }
35
 
36
- export const db = new MonsterDatabase();
 
1
  import Dexie, { type Table } from 'dexie';
2
+ import type { Monster, PicletInstance, Encounter, GameState } from './schema';
3
 
4
+ export class PicletDatabase extends Dexie {
5
+ // Legacy table
6
  monsters!: Table<Monster>;
7
+
8
+ // New game tables
9
+ picletInstances!: Table<PicletInstance>;
10
+ encounters!: Table<Encounter>;
11
+ gameState!: Table<GameState>;
12
 
13
  constructor() {
14
+ super('PicletGameDB');
15
 
16
  this.version(1).stores({
17
  monsters: '++id, name, createdAt'
 
36
  monster.stats = monster.stats || null;
37
  });
38
  });
39
+
40
+ // Version 4: Add new game tables
41
+ this.version(4).stores({
42
+ monsters: '++id, name, createdAt',
43
+ picletInstances: '++id, typeId, nickname, isInRoster, rosterPosition, caughtAt',
44
+ encounters: '++id, type, createdAt',
45
+ gameState: '++id, lastPlayed'
46
+ });
47
  }
48
  }
49
 
50
+ export const db = new PicletDatabase();
src/lib/db/monsters.ts CHANGED
@@ -1,13 +1,26 @@
1
  import { db } from './index';
2
  import type { Monster } from './schema';
 
3
 
4
- export async function saveMonster(monsterData: Omit<Monster, 'id' | 'createdAt'>): Promise<number> {
5
  const monster: Omit<Monster, 'id'> = {
6
  ...monsterData,
7
  createdAt: new Date()
8
  };
9
 
10
- return await db.monsters.add(monster);
 
 
 
 
 
 
 
 
 
 
 
 
11
  }
12
 
13
  export async function getAllMonsters(): Promise<Monster[]> {
 
1
  import { db } from './index';
2
  import type { Monster } from './schema';
3
+ import { monsterToPicletInstance, savePicletInstance } from './piclets';
4
 
5
+ export async function saveMonster(monsterData: Omit<Monster, 'id' | 'createdAt'>, createPiclet: boolean = true): Promise<number> {
6
  const monster: Omit<Monster, 'id'> = {
7
  ...monsterData,
8
  createdAt: new Date()
9
  };
10
 
11
+ const monsterId = await db.monsters.add(monster);
12
+
13
+ // Also create a PicletInstance if requested and stats are available
14
+ if (createPiclet && monster.stats) {
15
+ try {
16
+ const picletData = await monsterToPicletInstance({ ...monster, id: monsterId }, 5);
17
+ await savePicletInstance(picletData);
18
+ } catch (err) {
19
+ console.error('Failed to create PicletInstance:', err);
20
+ }
21
+ }
22
+
23
+ return monsterId;
24
  }
25
 
26
  export async function getAllMonsters(): Promise<Monster[]> {
src/lib/db/piclets.ts ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { db } from './index';
2
+ import type { PicletInstance, Monster, BattleMove } from './schema';
3
+
4
+ // Convert a generated Monster to a PicletInstance
5
+ export async function monsterToPicletInstance(monster: Monster, level: number = 5): Promise<Omit<PicletInstance, 'id'>> {
6
+ if (!monster.stats) {
7
+ throw new Error('Monster must have stats to create PicletInstance');
8
+ }
9
+
10
+ const stats = monster.stats;
11
+
12
+ // Calculate base stats from the 0-100 scale
13
+ const baseHp = Math.floor(stats.HP * 2 + 50);
14
+ const baseAttack = Math.floor(stats.attack * 1.5 + 30);
15
+ const baseDefense = Math.floor(stats.defence * 1.5 + 30);
16
+ const baseSpeed = Math.floor(stats.speed * 1.5 + 30);
17
+
18
+ // Field stats are variations of regular stats
19
+ const baseFieldAttack = Math.floor(baseAttack * 0.8);
20
+ const baseFieldDefense = Math.floor(baseDefense * 0.8);
21
+
22
+ // Calculate current stats based on level
23
+ const calculateStat = (base: number, level: number) => Math.floor((base * level) / 50 + 5);
24
+ const calculateHp = (base: number, level: number) => Math.floor((base * level) / 50 + level + 10);
25
+
26
+ const maxHp = calculateHp(baseHp, level);
27
+
28
+ // Create moves based on monster's abilities
29
+ const moves: BattleMove[] = [
30
+ {
31
+ name: stats.attackActionName,
32
+ type: 'normal',
33
+ power: 50,
34
+ accuracy: 95,
35
+ pp: 20,
36
+ currentPp: 20,
37
+ description: stats.attackActionDescription
38
+ },
39
+ {
40
+ name: stats.buffActionName,
41
+ type: 'status',
42
+ power: 0,
43
+ accuracy: 100,
44
+ pp: 15,
45
+ currentPp: 15,
46
+ description: stats.buffActionDescription
47
+ },
48
+ {
49
+ name: stats.debuffActionName,
50
+ type: 'status',
51
+ power: 0,
52
+ accuracy: 85,
53
+ pp: 15,
54
+ currentPp: 15,
55
+ description: stats.debuffActionDescription
56
+ },
57
+ {
58
+ name: stats.specialActionName,
59
+ type: 'special',
60
+ power: 80,
61
+ accuracy: 90,
62
+ pp: 5,
63
+ currentPp: 5,
64
+ description: stats.specialActionDescription
65
+ }
66
+ ];
67
+
68
+ const bst = baseHp + baseAttack + baseDefense + baseFieldAttack + baseFieldDefense + baseSpeed;
69
+
70
+ return {
71
+ // Type Info
72
+ typeId: monster.name.toLowerCase().replace(/\s+/g, '-'),
73
+ nickname: monster.name,
74
+ primaryTypeString: 'normal', // Default type, could be enhanced based on concept
75
+ secondaryTypeString: undefined,
76
+
77
+ // Current Stats
78
+ currentHp: maxHp,
79
+ maxHp,
80
+ level,
81
+ xp: 0,
82
+ attack: calculateStat(baseAttack, level),
83
+ defense: calculateStat(baseDefense, level),
84
+ fieldAttack: calculateStat(baseFieldAttack, level),
85
+ fieldDefense: calculateStat(baseFieldDefense, level),
86
+ speed: calculateStat(baseSpeed, level),
87
+
88
+ // Base Stats
89
+ baseHp,
90
+ baseAttack,
91
+ baseDefense,
92
+ baseFieldAttack,
93
+ baseFieldDefense,
94
+ baseSpeed,
95
+
96
+ // Battle
97
+ moves,
98
+ nature: 'hardy', // Default nature
99
+
100
+ // Roster
101
+ isInRoster: false,
102
+ rosterPosition: undefined,
103
+
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
+
111
+ // Original generation data
112
+ imageUrl: monster.imageUrl,
113
+ imageData: monster.imageData,
114
+ imageCaption: monster.imageCaption,
115
+ concept: monster.concept,
116
+ imagePrompt: monster.imagePrompt
117
+ };
118
+ }
119
+
120
+ // Save a new PicletInstance
121
+ export async function savePicletInstance(piclet: Omit<PicletInstance, 'id'>): Promise<number> {
122
+ return await db.picletInstances.add(piclet);
123
+ }
124
+
125
+ // Get all PicletInstances
126
+ export async function getAllPicletInstances(): Promise<PicletInstance[]> {
127
+ return await db.picletInstances.toArray();
128
+ }
129
+
130
+ // Get roster PicletInstances
131
+ export async function getRosterPiclets(): Promise<PicletInstance[]> {
132
+ return await db.picletInstances
133
+ .where('isInRoster')
134
+ .equals(1)
135
+ .sortBy('rosterPosition');
136
+ }
137
+
138
+ // Update roster position
139
+ export async function updateRosterPosition(id: number, position: number | undefined): Promise<void> {
140
+ await db.picletInstances.update(id, {
141
+ isInRoster: position !== undefined,
142
+ rosterPosition: position
143
+ });
144
+ }
145
+
146
+ // Delete a PicletInstance
147
+ export async function deletePicletInstance(id: number): Promise<void> {
148
+ await db.picletInstances.delete(id);
149
+ }
src/lib/db/schema.ts CHANGED
@@ -1,13 +1,132 @@
1
- import type { MonsterStats } from '$lib/types';
 
 
 
 
 
 
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  export interface Monster {
4
  id?: number;
5
  name: string;
6
  imageUrl: string;
7
- imageData?: string; // Base64 encoded image with transparency
8
  imageCaption: string;
9
  concept: string;
10
  imagePrompt: string;
11
- stats?: MonsterStats; // JSON stats object
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  createdAt: Date;
13
  }
 
1
+ // Enums
2
+ export enum EncounterType {
3
+ WILD_PICLET = 'wildPiclet',
4
+ TRAINER_BATTLE = 'trainerBattle',
5
+ SHOP = 'shop',
6
+ HEALTH_CENTER = 'healthCenter'
7
+ }
8
 
9
+ // Battle Move embedded object
10
+ export interface BattleMove {
11
+ name: string;
12
+ type: string;
13
+ power: number;
14
+ accuracy: number;
15
+ pp: number;
16
+ currentPp: number;
17
+ description: string;
18
+ }
19
+
20
+ // PicletInstance - Individual monster instances owned by the player
21
+ export interface PicletInstance {
22
+ id?: number;
23
+
24
+ // Type Info
25
+ typeId: string;
26
+ nickname?: string;
27
+ primaryTypeString: string;
28
+ secondaryTypeString?: string;
29
+
30
+ // Current Stats
31
+ currentHp: number;
32
+ maxHp: number;
33
+ level: number;
34
+ xp: number;
35
+ attack: number;
36
+ defense: number;
37
+ fieldAttack: number;
38
+ fieldDefense: number;
39
+ speed: number;
40
+
41
+ // Base Stats (from generation)
42
+ baseHp: number;
43
+ baseAttack: number;
44
+ baseDefense: number;
45
+ baseFieldAttack: number;
46
+ baseFieldDefense: number;
47
+ baseSpeed: number;
48
+
49
+ // Battle
50
+ moves: BattleMove[];
51
+ nature: string;
52
+
53
+ // Roster
54
+ isInRoster: boolean;
55
+ rosterPosition?: number; // 0-5 when in roster
56
+
57
+ // Metadata
58
+ caughtAt: Date;
59
+ bst: number; // Base Stat Total
60
+ tier: string;
61
+ role: string;
62
+ variance: number;
63
+
64
+ // Original generation data
65
+ imageUrl: string;
66
+ imageData?: string; // Base64 encoded image with transparency
67
+ imageCaption: string;
68
+ concept: string;
69
+ imagePrompt: string;
70
+ }
71
+
72
+ // Encounter - Game encounters
73
+ export interface Encounter {
74
+ id?: number;
75
+
76
+ // Type
77
+ type: EncounterType;
78
+
79
+ // Details
80
+ title: string;
81
+ description: string;
82
+ picletTypeId?: string; // For wild piclet encounters
83
+ enemyLevel?: number;
84
+
85
+ // Timing
86
+ createdAt: Date;
87
+ }
88
+
89
+ // GameState - Overall game progress
90
+ export interface GameState {
91
+ id?: number;
92
+
93
+ // Timing
94
+ lastEncounterRefresh: Date;
95
+ lastPlayed: Date;
96
+
97
+ // Progress (0-1000)
98
+ progressPoints: number;
99
+ trainersDefeated: number;
100
+ picletsCapured: number;
101
+ battlesLost: number;
102
+ }
103
+
104
+ // Legacy Monster interface for backward compatibility
105
  export interface Monster {
106
  id?: number;
107
  name: string;
108
  imageUrl: string;
109
+ imageData?: string;
110
  imageCaption: string;
111
  concept: string;
112
  imagePrompt: string;
113
+ stats?: {
114
+ name: string;
115
+ description: string;
116
+ rarity: number;
117
+ HP: number;
118
+ defence: number;
119
+ attack: number;
120
+ speed: number;
121
+ specialPassiveTrait: string;
122
+ attackActionName: string;
123
+ attackActionDescription: string;
124
+ buffActionName: string;
125
+ buffActionDescription: string;
126
+ debuffActionName: string;
127
+ debuffActionDescription: string;
128
+ specialActionName: string;
129
+ specialActionDescription: string;
130
+ };
131
  createdAt: Date;
132
  }