diff --git a/src/database/quests-seed.ts b/src/database/quests-seed.ts index 1d66902..c758717 100644 --- a/src/database/quests-seed.ts +++ b/src/database/quests-seed.ts @@ -86,6 +86,23 @@ export async function seedQuests(dataSource: DataSource) { }, ]; + // Side quests — fill the level gaps + const SIDE_QUESTS = [ + { name: 'Chasseur de champignons', description: 'Les Champis Vénéneux infestent les sous-bois. Éliminez-en 3.', objectiveType: 'kill_monster', objectiveTargetId: monsterMap.get('Champi Vénéneux') ?? null as string | null, objectiveCount: 3, rewardXp: 150, rewardGold: 40, rewardTitle: null, arcId: null, arcOrder: 0, minLevel: 2, repeatable: false }, + { name: 'La menace rampante', description: 'Les Serpents des Marais sont de plus en plus agressifs. Tuez-en 3.', objectiveType: 'kill_monster', objectiveTargetId: monsterMap.get('Serpent des Marais') ?? null as string | null, objectiveCount: 3, rewardXp: 180, rewardGold: 50, rewardTitle: null, arcId: null, arcOrder: 0, minLevel: 3, repeatable: false }, + { name: 'Guerrier éprouvé', description: 'Prouvez votre valeur en remportant 20 combats.', objectiveType: 'kill_any', objectiveTargetId: null as string | null, objectiveCount: 20, rewardXp: 250, rewardGold: 80, rewardTitle: null, arcId: null, arcOrder: 0, minLevel: 2, repeatable: false }, + { name: 'Collecteur de trophées', description: 'Remportez 50 combats au total.', objectiveType: 'kill_any', objectiveTargetId: null as string | null, objectiveCount: 50, rewardXp: 500, rewardGold: 200, rewardTitle: 'Vétéran', arcId: null, arcOrder: 0, minLevel: 3, repeatable: false }, + { name: 'Exterminateur', description: 'Éliminez 10 monstres de chaque espèce des marais.', objectiveType: 'kill_any', objectiveTargetId: null as string | null, objectiveCount: 40, rewardXp: 400, rewardGold: 150, rewardTitle: null, arcId: null, arcOrder: 0, minLevel: 4, repeatable: false }, + { name: 'Première forge', description: 'Améliorez un item à la forge 3 fois.', objectiveType: 'forge_item', objectiveTargetId: null as string | null, objectiveCount: 3, rewardXp: 120, rewardGold: 40, rewardTitle: null, arcId: null, arcOrder: 0, minLevel: 2, repeatable: false }, + ]; + + for (const data of SIDE_QUESTS) { + const existing = await questRepo.findOne({ where: { name: data.name } }); + if (!existing) { + await questRepo.save(questRepo.create(data)); + } + } + // Standalone repeatable quests (daily feel) const STANDALONE = [ { diff --git a/src/quest/quest.service.ts b/src/quest/quest.service.ts index 7ee4168..5acefbc 100644 --- a/src/quest/quest.service.ts +++ b/src/quest/quest.service.ts @@ -32,22 +32,27 @@ export class QuestService { const character = await this.characterRepo.findOne({ where: { id: characterId } }); if (!character) throw new BadRequestException('Aucun personnage'); - // All quests the player hasn't completed (or repeatable) - const completed = await this.playerQuestRepo.find({ - where: { characterId, status: 'claimed' }, - select: ['questId'], + // All player quests (any status) + const playerQuests = await this.playerQuestRepo.find({ + where: { characterId }, + select: ['questId', 'status'], }); - const completedIds = new Set(completed.map((pq) => pq.questId)); + const questStatusMap = new Map(playerQuests.map((pq) => [pq.questId, pq.status])); const quests = await this.questRepo.find({ - where: { minLevel: undefined }, // load all, filter in code relations: ['arc'], order: { arcOrder: 'ASC' }, }); return quests.filter((q) => { if (q.minLevel > character.level) return false; - if (completedIds.has(q.id) && !q.repeatable) return false; + + const status = questStatusMap.get(q.id); + // Already active or completed (waiting claim) → not available + if (status === 'active' || status === 'completed') return false; + // Already claimed and not repeatable → not available + if (status === 'claimed' && !q.repeatable) return false; + return true; }); }