feat: craft/drops — 10 matériaux, 12 recettes, drop rate variable
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 32s
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 32s
- 10 matériaux Égouts/Désert (Poil de Rat → Œil du Sphinx) - 12 items craftables dont 1 legendary (Sceptre Prophétique) - 12 recettes cross-zone avec ingrédients cohérents - 15 monstres mappés à leur drop (tous les Égouts/Désert) - Drop rate variable par difficulté relative (25-80%) - Quantité drop variable (1-3 selon boss/difficulté)
This commit is contained in:
@@ -19,6 +19,36 @@ import {
|
||||
} from './combat.engine';
|
||||
|
||||
const COMBAT_ENDURANCE_COST = 5;
|
||||
|
||||
/**
|
||||
* Drop rate variable basé sur la difficulté relative monstre vs joueur.
|
||||
* Monstre facile = moins de drop, monstre difficile = plus de drop + quantité.
|
||||
* Boss de zone (maxLevel ≥ 9 et spread ≥ 3) = 80% + 2-3 drops.
|
||||
*/
|
||||
function computeDropRate(
|
||||
playerLevel: number,
|
||||
monsterMinLevel: number,
|
||||
monsterMaxLevel: number,
|
||||
): { dropRate: number; dropQty: number } {
|
||||
const monsterAvgLevel = (monsterMinLevel + monsterMaxLevel) / 2;
|
||||
const diff = monsterAvgLevel - playerLevel;
|
||||
const isBoss = (monsterMaxLevel - monsterMinLevel) >= 3 && monsterMaxLevel >= 9;
|
||||
|
||||
if (isBoss) {
|
||||
return { dropRate: 0.80, dropQty: 2 + (Math.random() < 0.5 ? 1 : 0) }; // 2-3
|
||||
}
|
||||
if (diff >= 2) {
|
||||
return { dropRate: 0.60, dropQty: 1 + (Math.random() < 0.3 ? 1 : 0) }; // 1-2
|
||||
}
|
||||
if (diff >= 0) {
|
||||
return { dropRate: 0.50, dropQty: 1 + (Math.random() < 0.2 ? 1 : 0) }; // 1-2
|
||||
}
|
||||
if (diff >= -2) {
|
||||
return { dropRate: 0.40, dropQty: 1 };
|
||||
}
|
||||
// Très facile (level >> monstre)
|
||||
return { dropRate: 0.25, dropQty: 1 };
|
||||
}
|
||||
const DEFEAT_ENDURANCE_PENALTY = 25;
|
||||
const DEFEAT_HP_RATIO = 0.2; // 20% hpMax à la défaite
|
||||
const VICTORY_HP_REGEN_RATIO = 0.1; // +10% hpMax à la victoire
|
||||
@@ -166,13 +196,16 @@ export class CombatService {
|
||||
}
|
||||
}
|
||||
|
||||
// Loot matériaux — 40% de chance après victoire
|
||||
// Loot matériaux — drop rate variable par difficulté relative
|
||||
let lootMaterial: { name: string; quantity: number } | null = null;
|
||||
let lootedMaterialId: string | null = null;
|
||||
if (result.winner === 'player' && monster.dropMaterialId && Math.random() < 0.4) {
|
||||
await this.materialService.addMaterial(character.id, monster.dropMaterialId, 1);
|
||||
lootMaterial = { name: 'matériau', quantity: 1 };
|
||||
lootedMaterialId = monster.dropMaterialId;
|
||||
if (result.winner === 'player' && monster.dropMaterialId) {
|
||||
const { dropRate, dropQty } = computeDropRate(character.level, monster.minLevel, monster.maxLevel);
|
||||
if (Math.random() < dropRate) {
|
||||
await this.materialService.addMaterial(character.id, monster.dropMaterialId, dropQty);
|
||||
lootMaterial = { name: 'matériau', quantity: dropQty };
|
||||
lootedMaterialId = monster.dropMaterialId;
|
||||
}
|
||||
}
|
||||
|
||||
// Persister le log
|
||||
@@ -202,7 +235,7 @@ export class CombatService {
|
||||
}
|
||||
|
||||
if (lootMaterial) {
|
||||
summaryParts.push(`Loot : 1 matériau obtenu !`);
|
||||
summaryParts.push(`Loot : ${lootMaterial.quantity} matériau${lootMaterial.quantity > 1 ? 'x' : ''} obtenu${lootMaterial.quantity > 1 ? 's' : ''} !`);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user