File size: 8,055 Bytes
465b043
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7428b13
465b043
7428b13
465b043
7428b13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465b043
7428b13
 
 
 
465b043
 
 
 
 
 
 
 
 
 
 
 
 
7428b13
 
 
 
465b043
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7428b13
 
 
 
465b043
 
7428b13
465b043
 
7428b13
465b043
 
7428b13
 
 
 
 
 
 
465b043
 
 
 
 
 
 
 
 
7428b13
 
 
 
 
 
 
 
 
 
 
 
465b043
 
 
 
 
 
 
 
 
 
 
 
 
7428b13
 
 
 
 
 
465b043
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
import { db } from './index';
import type { Encounter, PicletInstance } from './schema';
import { EncounterType } from './schema';
import { getOrCreateGameState, markEncountersRefreshed } from './gameState';

// Configuration
const ENCOUNTER_REFRESH_HOURS = 2;
const MIN_WILD_ENCOUNTERS = 2;
const MAX_WILD_ENCOUNTERS = 3;
const LEVEL_VARIANCE = 2;

export class EncounterService {
  // Check if encounters should be refreshed
  static async shouldRefreshEncounters(): Promise<boolean> {
    const state = await getOrCreateGameState();
    const hoursSinceRefresh = (Date.now() - state.lastEncounterRefresh.getTime()) / (1000 * 60 * 60);
    return hoursSinceRefresh >= ENCOUNTER_REFRESH_HOURS;
  }

  // Force encounter refresh
  static async forceEncounterRefresh(): Promise<void> {
    await db.encounters.clear();
    await markEncountersRefreshed();
  }

  // Get current encounters
  static async getCurrentEncounters(): Promise<Encounter[]> {
    return await db.encounters
      .orderBy('createdAt')
      .reverse()
      .toArray();
  }

  // Clear all encounters
  static async clearEncounters(): Promise<void> {
    await db.encounters.clear();
  }

  // Generate new encounters
  static async generateEncounters(): Promise<Encounter[]> {
    const encounters: Omit<Encounter, 'id'>[] = [];
    
    // Check if player has caught any piclets first
    const playerPiclets = await db.picletInstances.toArray();
    
    if (playerPiclets.length === 0) {
      // No piclets caught yet - check for discovered piclets
      // For now, we'll check the monsters collection as a proxy for discovered piclets
      // In a real app, this would check a remote database for piclets discovered by scanning
      const discoveredPiclets = await db.monsters.toArray();
      
      if (discoveredPiclets.length === 0) {
        // No piclets discovered yet - return empty encounters
        await db.encounters.clear();
        await markEncountersRefreshed();
        return [];
      }
      
      // Player has discovered but not caught any piclets - show ONLY first catch encounter
      const firstDiscovered = discoveredPiclets[0];
      encounters.push({
        type: EncounterType.WILD_PICLET,
        title: 'Your First Piclet!',
        description: 'A friendly piclet appears! This one seems easy to catch.',
        picletTypeId: firstDiscovered.id?.toString() || 'starter-001',
        enemyLevel: 5,
        createdAt: new Date()
      });
      
      // IMPORTANT: Return here - don't add shop/health center for first catch
      await db.encounters.clear();
      await db.encounters.add(encounters[0]);
      await markEncountersRefreshed();
      return await this.getCurrentEncounters();
    }
    
    // Player has piclets - generate normal encounters
    
    // Always add shop and health center first
    encounters.push({
      type: EncounterType.SHOP,
      title: 'Piclet Shop',
      description: 'Buy items and supplies for your journey',
      createdAt: new Date()
    });

    encounters.push({
      type: EncounterType.HEALTH_CENTER,
      title: 'Health Center',
      description: 'Heal your piclets back to full health',
      createdAt: new Date()
    });
    
    // Generate wild piclet encounters
    const wildEncounters = await this.generateWildEncounters();
    encounters.push(...wildEncounters);

    // Clear existing encounters and add new ones
    await db.encounters.clear();
    for (const encounter of encounters) {
      await db.encounters.add(encounter);
    }

    await markEncountersRefreshed();
    return await this.getCurrentEncounters();
  }

  // Create first catch encounter
  private static async createFirstCatchEncounter(): Promise<Omit<Encounter, 'id'>> {
    // TODO: Replace with actual piclet data when available
    // For now, using placeholder data
    return {
      type: EncounterType.WILD_PICLET,
      title: 'Your First Piclet!',
      description: 'A friendly piclet appears! This one seems easy to catch.',
      picletTypeId: 'starter-001', // Placeholder ID
      enemyLevel: 5,
      createdAt: new Date()
    };
  }

  // Generate wild piclet encounters
  private static async generateWildEncounters(): Promise<Omit<Encounter, 'id'>[]> {
    const encounters: Omit<Encounter, 'id'>[] = [];
    
    // Get player's average level
    const avgLevel = await this.getPlayerAverageLevel();
    
    // TODO: When piclet types are available, filter for uncaught piclets
    // For now, generate placeholder encounters
    const encounterCount = MIN_WILD_ENCOUNTERS + Math.floor(Math.random() * (MAX_WILD_ENCOUNTERS - MIN_WILD_ENCOUNTERS + 1));
    
    for (let i = 0; i < encounterCount; i++) {
      const levelVariance = Math.floor(Math.random() * (LEVEL_VARIANCE * 2 + 1)) - LEVEL_VARIANCE;
      const enemyLevel = Math.max(1, avgLevel + levelVariance);
      
      encounters.push({
        type: EncounterType.WILD_PICLET,
        title: `Wild Piclet Appeared!`,
        description: `A level ${enemyLevel} piclet blocks your path!`,
        picletTypeId: `wild-${i + 1}`, // Placeholder ID
        enemyLevel,
        createdAt: new Date()
      });
    }
    
    return encounters;
  }

  // Get player's average piclet level
  private static async getPlayerAverageLevel(): Promise<number> {
    const rosterPiclets = await db.picletInstances
      .where('isInRoster')
      .equals(1) // Dexie uses 1 for true in indexed fields
      .toArray();
    
    if (rosterPiclets.length === 0) {
      const allPiclets = await db.picletInstances.toArray();
      if (allPiclets.length === 0) return 5; // Default starting level
      
      const totalLevel = allPiclets.reduce((sum, p) => sum + p.level, 0);
      return Math.round(totalLevel / allPiclets.length);
    }
    
    const totalLevel = rosterPiclets.reduce((sum, p) => sum + p.level, 0);
    return Math.round(totalLevel / rosterPiclets.length);
  }

  // Catch a wild piclet (for first encounter)
  static async catchWildPiclet(encounter: Encounter): Promise<PicletInstance> {
    // Get the discovered piclet data
    const discoveredPiclets = await db.monsters.toArray();
    const picletData = discoveredPiclets.find(p => p.id?.toString() === encounter.picletTypeId) || discoveredPiclets[0];
    
    const newPiclet: Omit<PicletInstance, 'id'> = {
      typeId: encounter.picletTypeId!,
      nickname: picletData?.name || 'Starter Piclet',
      primaryTypeString: 'normal',
      
      // Stats based on level
      level: encounter.enemyLevel || 5,
      xp: 0,
      currentHp: 20 + (encounter.enemyLevel || 5) * 4,
      maxHp: 20 + (encounter.enemyLevel || 5) * 4,
      attack: 10 + (encounter.enemyLevel || 5) * 2,
      defense: 10 + (encounter.enemyLevel || 5) * 2,
      fieldAttack: 10 + (encounter.enemyLevel || 5) * 2,
      fieldDefense: 10 + (encounter.enemyLevel || 5) * 2,
      speed: 10 + (encounter.enemyLevel || 5) * 2,
      
      // Base stats
      baseHp: 20,
      baseAttack: 10,
      baseDefense: 10,
      baseFieldAttack: 10,
      baseFieldDefense: 10,
      baseSpeed: 10,
      
      // Battle moves
      moves: [
        {
          name: 'Tackle',
          type: 'normal',
          power: 40,
          accuracy: 100,
          pp: 35,
          currentPp: 35,
          description: 'A physical attack'
        }
      ],
      nature: 'hardy',
      
      // Roster
      isInRoster: true,
      rosterPosition: 0,
      
      // Metadata
      caughtAt: new Date(),
      bst: 60,
      tier: 'common',
      role: 'balanced',
      variance: 1,
      
      // Visuals from discovered data
      imageUrl: picletData?.imageUrl || `https://storage.googleapis.com/piclodia/${encounter.picletTypeId}.png`,
      imageData: picletData?.imageData,
      imageCaption: picletData?.imageCaption || 'A friendly starter piclet',
      concept: picletData?.concept || 'starter',
      imagePrompt: picletData?.imagePrompt || 'cute starter monster'
    };
    
    const id = await db.picletInstances.add(newPiclet);
    return { ...newPiclet, id };
  }
}