All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 33s
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).
231 lines
9.8 KiB
TypeScript
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)');
|
|
}
|