Files
TetaRdPG/src/database/quests-seed.ts
Tetardtek d1609efaae
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 33s
feat: zone locking — progression par arcs narratifs + arcs Égouts/Désert
Zones verrouillées: marais toujours ouvert, égouts après arc Marais,
désert après arc Égouts. Filtrage backend sur monstres ET boutique.

Arc "Les Égouts de la Cité" (4 quêtes, lv4-7, boss Roi des Rats)
Arc "Les Sables Brûlants" (3 quêtes, lv8-12, boss Sphinx)

GET /api/monsters/zones — retourne les zones avec statut unlocked.
Combat page: monstres groupés par zone, zones lockées avec icône cadenas.
Boutique: items filtrés par zones débloquées (potions toujours visibles).
2026-03-24 17:57:23 +01:00

231 lines
9.8 KiB
TypeScript

import { DataSource } from 'typeorm';
import { QuestArc } from '../quest/quest-arc.entity';
import { Quest } from '../quest/quest.entity';
import { Achievement } from '../achievement/achievement.entity';
export async function seedQuests(dataSource: DataSource) {
const arcRepo = dataSource.getRepository(QuestArc);
const questRepo = dataSource.getRepository(Quest);
const achievementRepo = dataSource.getRepository(Achievement);
// --- Arc 1: Les Marais du Têtard ---
let arc = await arcRepo.findOne({ where: { name: 'Les Marais du Têtard' } });
if (!arc) {
arc = await arcRepo.save(arcRepo.create({
name: 'Les Marais du Têtard',
description: 'Les marais grouillent de créatures hostiles. Nettoyez la zone pour prouver votre valeur.',
zone: 'marais',
sortOrder: 1,
minLevel: 1,
}));
}
// Get monster IDs for targeted quests
const monsters = await dataSource.query('SELECT id, name FROM monsters');
const monsterMap = new Map<string, string>(monsters.map((m: any) => [m.name, m.id]));
const materials = await dataSource.query('SELECT id, name FROM materials');
const materialMap = new Map<string, string>(materials.map((m: any) => [m.name, m.id]));
const QUESTS = [
{
name: 'Premiers pas',
description: 'Éliminez 3 Têtards Vase pour sécuriser les abords du village.',
objectiveType: 'kill_monster',
objectiveTargetId: monsterMap.get('Têtard Vase') ?? null as string | null,
objectiveCount: 3,
rewardXp: 100,
rewardGold: 30,
rewardTitle: null,
arcId: arc.id,
arcOrder: 1,
minLevel: 1,
repeatable: false,
},
{
name: 'Chasseur de grenouilles',
description: 'Les Grenouilles Boueuses menacent les récoltes. Abattez-en 5.',
objectiveType: 'kill_monster',
objectiveTargetId: monsterMap.get('Grenouille Boueuse') ?? null as string | null,
objectiveCount: 5,
rewardXp: 200,
rewardGold: 60,
rewardTitle: null,
arcId: arc.id,
arcOrder: 2,
minLevel: 2,
repeatable: false,
},
{
name: 'Apprenti combattant',
description: 'Prouvez votre endurance en remportant 10 combats.',
objectiveType: 'kill_any',
objectiveTargetId: null,
objectiveCount: 10,
rewardXp: 150,
rewardGold: 50,
rewardTitle: null,
arcId: arc.id,
arcOrder: 3,
minLevel: 1,
repeatable: false,
},
{
name: 'Le gardien des marais',
description: 'Le Golem de Boue terrorise les marais depuis trop longtemps. Mettez fin à son règne.',
objectiveType: 'kill_monster',
objectiveTargetId: monsterMap.get('Golem de Boue') ?? null as string | null,
objectiveCount: 1,
rewardXp: 500,
rewardGold: 200,
rewardTitle: 'Nettoyeur des Marais',
arcId: arc.id,
arcOrder: 4,
minLevel: 5,
repeatable: false,
},
];
// Standalone repeatable quests (daily feel)
const STANDALONE = [
{
name: 'Chasse du jour',
description: 'Remportez 5 combats.',
objectiveType: 'kill_any',
objectiveTargetId: null,
objectiveCount: 5,
rewardXp: 80,
rewardGold: 25,
rewardTitle: null,
arcId: null,
arcOrder: 0,
minLevel: 1,
repeatable: true,
},
{
name: 'Apprenti forgeron',
description: 'Améliorez un item à la forge.',
objectiveType: 'forge_item',
objectiveTargetId: null,
objectiveCount: 1,
rewardXp: 60,
rewardGold: 20,
rewardTitle: null,
arcId: null,
arcOrder: 0,
minLevel: 1,
repeatable: true,
},
{
name: 'Artisan du jour',
description: 'Complétez un craft.',
objectiveType: 'craft_item',
objectiveTargetId: null,
objectiveCount: 1,
rewardXp: 50,
rewardGold: 15,
rewardTitle: null,
arcId: null,
arcOrder: 0,
minLevel: 1,
repeatable: true,
},
];
for (const data of [...QUESTS, ...STANDALONE]) {
const existing = await questRepo.findOne({ where: { name: data.name } });
if (!existing) {
await questRepo.save(questRepo.create(data));
}
}
// --- Quest achievements ---
const QUEST_ACHIEVEMENTS = [
{ key: 'quests_5', name: 'Aventurier Curieux', description: 'Compléter 5 quêtes', category: 'progression', tier: 'bronze', criteriaType: 'quests_completed', criteriaValue: 5, rewardGold: 100, rewardTitle: null },
{ key: 'quests_25', name: 'Héros du Peuple', description: 'Compléter 25 quêtes', category: 'progression', tier: 'silver', criteriaType: 'quests_completed', criteriaValue: 25, rewardGold: 500, rewardTitle: 'Héros du Peuple' },
{ key: 'quests_100', name: 'Légende Quêteuse', description: 'Compléter 100 quêtes', category: 'progression', tier: 'gold', criteriaType: 'quests_completed', criteriaValue: 100, rewardGold: 2000, rewardTitle: 'Légende Quêteuse' },
{ key: 'arc_1', name: 'Premier Arc', description: 'Compléter un arc narratif', category: 'progression', tier: 'bronze', criteriaType: 'quest_arc_completed', criteriaValue: 1, rewardGold: 300, rewardTitle: null },
{ key: 'arc_3', name: 'Conteur', description: 'Compléter 3 arcs narratifs', category: 'progression', tier: 'silver', criteriaType: 'quest_arc_completed', criteriaValue: 3, rewardGold: 1000, rewardTitle: 'Conteur' },
];
for (const data of QUEST_ACHIEVEMENTS) {
const existing = await achievementRepo.findOne({ where: { key: data.key } });
if (!existing) {
await achievementRepo.save(achievementRepo.create(data));
}
}
console.log(`✅ 1 arc + ${QUESTS.length + STANDALONE.length} quêtes + ${QUEST_ACHIEVEMENTS.length} achievements seedés`);
// --- Arc 2: Les Égouts ---
let arcEgouts = await arcRepo.findOne({ where: { name: 'Les Égouts de la Cité' } });
if (!arcEgouts) {
arcEgouts = await arcRepo.save(arcRepo.create({
name: 'Les Égouts de la Cité',
description: 'Les égouts grouillent de créatures répugnantes. Nettoyez ces tunnels oubliés.',
zone: 'egouts',
sortOrder: 2,
minLevel: 4,
}));
const ratId = monsterMap.get("Rat d'Égout") ?? null as string | null;
const crocoId = monsterMap.get('Crocodile') ?? null as string | null;
const roiId = monsterMap.get('Roi des Rats') ?? null as string | null;
const EGOUTS_QUESTS = [
{ name: 'Dératisation', description: 'Les rats pullulent. Éliminez-en 5.', objectiveType: 'kill_monster', objectiveTargetId: ratId, objectiveCount: 5, rewardXp: 200, rewardGold: 80, rewardTitle: null, arcId: arcEgouts.id, arcOrder: 1, minLevel: 4, repeatable: false },
{ name: 'Chasseur des profondeurs', description: 'Remportez 15 combats dans les égouts.', objectiveType: 'kill_any', objectiveTargetId: null as string | null, objectiveCount: 15, rewardXp: 300, rewardGold: 120, rewardTitle: null, arcId: arcEgouts.id, arcOrder: 2, minLevel: 5, repeatable: false },
{ name: 'Le reptile', description: 'Terrassez 3 Crocodiles qui rôdent dans les canaux.', objectiveType: 'kill_monster', objectiveTargetId: crocoId, objectiveCount: 3, rewardXp: 400, rewardGold: 150, rewardTitle: null, arcId: arcEgouts.id, arcOrder: 3, minLevel: 6, repeatable: false },
{ name: 'Le Roi des Rats', description: 'Mettez fin au règne du Roi des Rats.', objectiveType: 'kill_monster', objectiveTargetId: roiId, objectiveCount: 1, rewardXp: 800, rewardGold: 400, rewardTitle: 'Nettoyeur des Égouts', arcId: arcEgouts.id, arcOrder: 4, minLevel: 7, repeatable: false },
];
for (const q of EGOUTS_QUESTS) {
await questRepo.save(questRepo.create(q));
}
console.log(`✅ Arc Égouts + ${EGOUTS_QUESTS.length} quêtes seedés`);
}
// --- Arc 3: Le Désert ---
let arcDesert = await arcRepo.findOne({ where: { name: 'Les Sables Brûlants' } });
if (!arcDesert) {
arcDesert = await arcRepo.save(arcRepo.create({
name: 'Les Sables Brûlants',
description: 'Le désert cache des trésors anciens et des créatures redoutables.',
zone: 'desert',
sortOrder: 3,
minLevel: 8,
}));
const scorpId = monsterMap.get('Scorpion') ?? null as string | null;
const sphinxId = monsterMap.get('Sphinx') ?? null as string | null;
const DESERT_QUESTS = [
{ name: 'Piqûres mortelles', description: 'Éliminez 5 Scorpions.', objectiveType: 'kill_monster', objectiveTargetId: scorpId, objectiveCount: 5, rewardXp: 400, rewardGold: 150, rewardTitle: null, arcId: arcDesert.id, arcOrder: 1, minLevel: 8, repeatable: false },
{ name: 'Survivant du désert', description: 'Remportez 20 combats dans le désert.', objectiveType: 'kill_any', objectiveTargetId: null as string | null, objectiveCount: 20, rewardXp: 600, rewardGold: 250, rewardTitle: null, arcId: arcDesert.id, arcOrder: 2, minLevel: 9, repeatable: false },
{ name: 'L\'énigme du Sphinx', description: 'Terrassez le Sphinx — gardien des sables.', objectiveType: 'kill_monster', objectiveTargetId: sphinxId, objectiveCount: 1, rewardXp: 1500, rewardGold: 800, rewardTitle: 'Conquérant du Désert', arcId: arcDesert.id, arcOrder: 3, minLevel: 12, repeatable: false },
];
for (const q of DESERT_QUESTS) {
await questRepo.save(questRepo.create(q));
}
console.log(`✅ Arc Désert + ${DESERT_QUESTS.length} quêtes seedés`);
}
}
// Update monster XP rewards (nerf for quest-driven progression)
export async function nerfMonsterXp(dataSource: DataSource) {
const updates = [
{ name: 'Têtard Vase', xpReward: 8 },
{ name: 'Grenouille Boueuse', xpReward: 15 },
{ name: 'Serpent des Marais', xpReward: 25 },
{ name: 'Champi Vénéneux', xpReward: 20 },
{ name: 'Golem de Boue', xpReward: 50 },
];
for (const u of updates) {
await dataSource.query('UPDATE monsters SET xp_reward = ? WHERE name = ?', [u.xpReward, u.name]);
}
console.log('✅ XP monstres ajustée (nerf pour progression par quêtes)');
}