From 9fac9e123bad3d01d3fec8bed83273fa3d5502bd Mon Sep 17 00:00:00 2001 From: Tetardtek Date: Tue, 24 Mar 2026 16:57:57 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20repeatable=20quests=20hors=20pool=203?= =?UTF-8?q?=20slots=20+=20section=20t=C3=A2ches=20quotidiennes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Répétables ne comptent plus dans le MAX_ACTIVE_QUESTS (3). Frontend: section séparée "Tâches quotidiennes" en grille 3 colonnes, quêtes narratives en haut avec les slots limités. Prépare le terrain pour le hub village (forgeron, taverne, etc.). --- frontend/src/pages/QuestPage.tsx | 33 +++++++++++++++++++++++--------- src/quest/quest.service.ts | 18 +++++++++++------ 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/frontend/src/pages/QuestPage.tsx b/frontend/src/pages/QuestPage.tsx index c186d3c..1b53bce 100644 --- a/frontend/src/pages/QuestPage.tsx +++ b/frontend/src/pages/QuestPage.tsx @@ -168,37 +168,41 @@ export function QuestPage() { if (loadActive || loadAvail) return
Chargement…
; - const activeCount = active?.length ?? 0; + // Séparer quêtes narratives (slots limités) et répétables (toujours en fond) + const activeStory = active?.filter((pq: any) => !pq.quest.repeatable) ?? []; + const activeDaily = active?.filter((pq: any) => pq.quest.repeatable) ?? []; + const availableStory = available?.filter((q: any) => !q.repeatable) ?? []; + const availableDaily = available?.filter((q: any) => q.repeatable) ?? []; return (

📜 Quêtes

- {/* Active quests */} + {/* Active story quests */}

- Quêtes actives ({activeCount}/3) + Quêtes actives ({activeStory.length}/3)

- {active && active.length > 0 ? ( + {activeStory.length > 0 ? (
- {active.map((pq: any) => )} + {activeStory.map((pq: any) => )}
) : (
- Aucune quête active — acceptez-en dans le panneau de droite + Aucune quête active — acceptez-en à droite
)}
- {/* Available quests */} + {/* Available story quests */}

Quêtes disponibles

- {available && available.length > 0 ? ( + {availableStory.length > 0 ? (
- {available.map((q: any) => )} + {availableStory.map((q: any) => )}
) : (
@@ -208,6 +212,17 @@ export function QuestPage() {
+ {/* Tâches quotidiennes (répétables — toujours en fond) */} +
+

+ 🔄 Tâches quotidiennes +

+
+ {activeDaily.map((pq: any) => )} + {availableDaily.map((q: any) => )} +
+
+ {/* Arcs narratifs */} {arcs && arcs.length > 0 && (
diff --git a/src/quest/quest.service.ts b/src/quest/quest.service.ts index 0241e04..7ee4168 100644 --- a/src/quest/quest.service.ts +++ b/src/quest/quest.service.ts @@ -83,12 +83,18 @@ export class QuestService { throw new BadRequestException(`Niveau ${quest.minLevel} requis`); } - // Check active quest count - const activeCount = await this.playerQuestRepo.count({ - where: { characterId, status: 'active' }, - }); - if (activeCount >= MAX_ACTIVE_QUESTS) { - throw new BadRequestException(`Maximum ${MAX_ACTIVE_QUESTS} quêtes actives`); + // Check active quest count (repeatable quests don't count toward the limit) + if (!quest.repeatable) { + const activeNonRepeatable = await this.playerQuestRepo + .createQueryBuilder('pq') + .innerJoin('pq.quest', 'q') + .where('pq.character_id = :characterId', { characterId }) + .andWhere('pq.status = :status', { status: 'active' }) + .andWhere('q.repeatable = false') + .getCount(); + if (activeNonRepeatable >= MAX_ACTIVE_QUESTS) { + throw new BadRequestException(`Maximum ${MAX_ACTIVE_QUESTS} quêtes actives (hors répétables)`); + } } // Check not already active