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.
49 lines
1.9 KiB
TypeScript
49 lines
1.9 KiB
TypeScript
import { Repository } from 'typeorm';
|
|
import { PlayerQuestArc } from '../quest/player-quest-arc.entity';
|
|
import { QuestArc } from '../quest/quest-arc.entity';
|
|
|
|
// Zone unlock chain: each zone requires completing the previous zone's arc
|
|
// marais → always open (L'Étang — niv 1-5)
|
|
// egouts → requires "Les Marais du Têtard" arc (L'Étang profond — niv 6-10)
|
|
// desert → requires egouts arc (L'Étang Brisé — niv 11-15)
|
|
// ruisseau_miroir → requires desert arc (Ruisseau Miroir — niv 16-18)
|
|
// marais_murmures → requires ruisseau_miroir arc (Marais des Murmures — niv 19-21)
|
|
// torrent_brise → requires marais_murmures arc (Torrent Brisé — niv 22-24)
|
|
// source_courant → requires torrent_brise arc (Source du Courant — niv 25+)
|
|
const ZONE_ORDER = ['marais', 'egouts', 'desert', 'ruisseau_miroir', 'marais_murmures', 'torrent_brise', 'source_courant'];
|
|
|
|
export async function getUnlockedZones(
|
|
characterId: string,
|
|
arcRepo: Repository<QuestArc>,
|
|
playerArcRepo: Repository<PlayerQuestArc>,
|
|
): Promise<string[]> {
|
|
const unlocked: string[] = ['marais']; // always accessible
|
|
|
|
// Get all completed arcs for this character
|
|
const completedArcs = await playerArcRepo.find({
|
|
where: { characterId, completed: true },
|
|
relations: ['questArc'],
|
|
});
|
|
const completedArcZones = new Set(completedArcs.map(pa => pa.questArc?.zone).filter(Boolean));
|
|
|
|
// Check zone chain: each zone unlocks the next
|
|
for (let i = 0; i < ZONE_ORDER.length - 1; i++) {
|
|
const currentZone = ZONE_ORDER[i];
|
|
const nextZone = ZONE_ORDER[i + 1];
|
|
|
|
// Find arc for current zone
|
|
const arc = await arcRepo.findOne({ where: { zone: currentZone } });
|
|
if (!arc) continue;
|
|
|
|
// If this zone's arc is completed, unlock the next zone
|
|
const isCompleted = completedArcs.some(pa => pa.questArcId === arc.id);
|
|
if (isCompleted) {
|
|
unlocked.push(nextZone);
|
|
} else {
|
|
break; // Can't skip zones
|
|
}
|
|
}
|
|
|
|
return unlocked;
|
|
}
|