feat: Phase 3 Lore & Contenu — L'Odyssée d'un têtard

Lore Bible (canon narratif complet) + Engine Design (séparation moteur/univers).

4 nouvelles zones (Ruisseau Miroir, Marais des Murmures, Torrent Brisé, Source du Courant)
dans la chaîne d'unlock après desert (niv 16-25+).

Module NPC complet (entity, service, controller) — 8 PNJ avec dialogues évolutifs
par palier de niveau : Gorn (niv 1-15), Pierre-Mémoire (niv 16+), Mira, Vell,
La Batracienne, Le Forgeron, Le Marchand.

20 monstres lore-friendly, 12 matériaux, 15 items (dont Bâton de Gorn légendaire).

17 quêtes narratives (4 arcs ch.9-12) avec textes acceptText/completeText
qui racontent l'Odyssée. Nouveau type story_event pour les moments narratifs purs.
3 quêtes répétables optionnelles.

Seed runner : npm run seed:odyssee

Tout est additif — zéro impact sur le contenu existant niv 1-15.
This commit is contained in:
2026-03-25 00:43:26 +01:00
parent 2c94e4f3aa
commit 4beb1b2ed9
16 changed files with 1520 additions and 10 deletions

View File

@@ -19,9 +19,17 @@ export class Quest {
@Column('text')
description: string;
/** Texte narratif affiché quand le joueur accepte la quête (le PNJ parle) */
@Column({ name: 'accept_text', type: 'text', nullable: true })
acceptText: string | null;
/** Texte narratif affiché quand la quête est complétée (conclusion de la scène) */
@Column({ name: 'complete_text', type: 'text', nullable: true })
completeText: string | null;
// Objectif
@Column({ name: 'objective_type', length: 30 })
objectiveType: string; // 'kill_monster' | 'kill_any' | 'gather_material' | 'craft_item' | 'forge_item'
objectiveType: string; // 'kill_monster' | 'kill_any' | 'gather_material' | 'craft_item' | 'forge_item' | 'story_event'
@Column({ name: 'objective_target_id', type: 'varchar', length: 255, nullable: true })
objectiveTargetId: string | null; // monster ID or material ID (null for kill_any)

View File

@@ -128,11 +128,15 @@ export class QuestService {
return this.playerQuestRepo.save(existing);
}
// story_event quests complete immediately — they're narrative moments, not grinds
const isStoryEvent = quest.objectiveType === 'story_event';
const pq = this.playerQuestRepo.create({
characterId,
questId,
progress: 0,
status: 'active',
progress: isStoryEvent ? 1 : 0,
status: isStoryEvent ? 'completed' : 'active',
completedAt: isStoryEvent ? new Date() : null,
});
return this.playerQuestRepo.save(pq);
}