diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index a41d5fa..024ac69 100644 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -7,6 +7,7 @@ export interface User { export interface Character { id: string; + userId: string; name: string; level: number; xp: number; @@ -18,21 +19,23 @@ export interface Character { vitalite: number; hpCurrent: number; hpMax: number; - endurance: number; - enduranceCurrent: number; // calculé à la lecture (backend field) + enduranceSaved: number; + lastEnduranceTs: string; enduranceMax: number; + enduranceCurrent: number; statPoints: number; - xpToNextLevel: number; activeTitle: string | null; totalGoldEarned: number; + xpToNextLevel: number; createdAt: string; + updatedAt: string; } export interface Monster { id: string; name: string; - levelMin: number; - levelMax: number; + minLevel: number; + maxLevel: number; hp: number; attack: number; defense: number; @@ -40,37 +43,57 @@ export interface Monster { xpReward: number; goldMin: number; goldMax: number; + dropMaterialId: string | null; } export interface CombatRound { round: number; - playerDamage: number; - playerCrit: boolean; - monsterDodged: boolean; - monsterDamage: number; - playerDodged: boolean; + playerAttack: { damage: number; isCrit: boolean; isDodged: boolean; log: string }; + monsterAttack: { damage: number; isCrit: boolean; isDodged: boolean; log: string }; playerHp: number; monsterHp: number; log: string[]; } +export interface CombatRewards { + xp: number; + gold: number; + goldLost: number; + levelUp: boolean; + newLevel: number; + statPointsGained: number; + loot: { name: string; quantity: number } | null; +} + +export interface CombatCharacterState { + level: number; + xp: number; + xpToNextLevel: number; + gold: number; + hpCurrent: number; + hpMax: number; + enduranceCurrent: number; + enduranceMax: number; + statPoints: number; +} + export interface CombatResult { winner: 'player' | 'monster'; rounds: CombatRound[]; - xpGained?: number; - goldGained?: number; - enduranceCost: number; - loot?: { material: Material; quantity: number } | null; + summary: string; + rewards: CombatRewards; + character: CombatCharacterState; } export interface CombatLog { id: string; - monsterId: string; - monsterName?: string; winner: 'player' | 'monster'; - xpGained: number; - goldGained: number; + totalRounds: number; + xpEarned: number; + goldEarned: number; + levelUp: boolean; createdAt: string; + monster: { id: string; name: string; minLevel: number; maxLevel: number }; } export type Rarity = 'common' | 'rare' | 'epic' | 'legendary'; diff --git a/frontend/src/components/HudBar.tsx b/frontend/src/components/HudBar.tsx index 93011ce..ab4f9ff 100644 --- a/frontend/src/components/HudBar.tsx +++ b/frontend/src/components/HudBar.tsx @@ -46,8 +46,8 @@ export function HudBar() { if (!char) return null; - const endurance = (char as any).enduranceCurrent ?? (char as any).endurance ?? 0; - const xpNext = (char as any).xpToNextLevel ?? Math.round(100 * Math.pow(char.level, 1.5)); + const endurance = char.enduranceCurrent; + const xpNext = char.xpToNextLevel; const questCount = activeQuests?.filter((pq: any) => pq.status === 'active').length ?? 0; const questReady = activeQuests?.filter((pq: any) => pq.status === 'completed').length ?? 0; @@ -88,11 +88,11 @@ export function HudBar() { {endurance}/{char.enduranceMax} - {(char as any).lastEnduranceTs && ( + {char.lastEnduranceTs && ( )} diff --git a/frontend/src/pages/CombatPage.tsx b/frontend/src/pages/CombatPage.tsx index 8f149d9..478909b 100644 --- a/frontend/src/pages/CombatPage.tsx +++ b/frontend/src/pages/CombatPage.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { combatApi, characterApi } from '../api/endpoints'; -import type { Monster, CombatResult } from '../api/types'; +import type { Monster, CombatResult, CombatLog } from '../api/types'; import { Swords, Trophy, Skull, Clock, Zap } from 'lucide-react'; const COMBAT_COST = 5; @@ -12,16 +12,21 @@ const ATTACK_TYPES = [ { id: 'magic', label: 'Magie', emoji: '✨', stat: 'Intelligence × 1.5' }, ]; -function MonsterCard({ m, selected, onSelect }: { m: Monster; selected: boolean; onSelect: () => void }) { +function MonsterCard({ m, selected, onSelect, playerLevel }: { m: Monster; selected: boolean; onSelect: () => void; playerLevel: number }) { + const tooHard = m.minLevel > playerLevel + 2; + const tooEasy = m.maxLevel < playerLevel - 3; + return (
{m.name} - Niv. {(m as any).minLevel ?? m.levelMin}–{(m as any).maxLevel ?? m.levelMax} + + Niv. {m.minLevel}–{m.maxLevel} +
❤️ {m.hp} @@ -30,49 +35,49 @@ function MonsterCard({ m, selected, onSelect }: { m: Monster; selected: boolean; ⭐ {m.xpReward} XP 💰 {m.goldMin}–{m.goldMax}
+ {tooHard &&
Niveau trop élevé
}
); } -function CombatLog({ result }: { result: CombatResult }) { +function CombatLogView({ result }: { result: CombatResult }) { const won = result.winner === 'player'; return (
- {/* Résultat */}
{won ?
- Victoire ! +{(result as any).rewards?.xp ?? result.xpGained} XP +{(result as any).rewards?.gold ?? result.goldGained} or + Victoire ! +{result.rewards.xp} XP +{result.rewards.gold} or
:
- Défaite… −50 endurance + Défaite… Retour à l'auberge
} - {((result as any).rewards?.loot ?? result.loot) && ( + {result.rewards.loot && (
- 🎁 Loot obtenu ! + 🎁 Loot : {result.rewards.loot.name} ×{result.rewards.loot.quantity}
)} - {(result as any).rewards?.levelUp && ( + {result.rewards.levelUp && (
- 🎉 LEVEL UP ! Niveau {(result as any).rewards.newLevel} — +{(result as any).rewards.statPointsGained} points de stats + 🎉 LEVEL UP ! Niveau {result.rewards.newLevel} — +{result.rewards.statPointsGained} points de stats
)}
- {/* Log de combat */}

Log — {result.rounds.length} tour{result.rounds.length > 1 ? 's' : ''}

{result.rounds.flatMap(r => r.log.map((line, i) => { - const cls = line.includes('frappe') && !line.includes('Monstre') ? 'log-player' - : line.includes('Monstre') || line.includes('frappe') ? 'log-monster' - : line.includes('CRITIQUE') ? 'log-crit' - : 'log-system'; + const cls = line.includes('CRITIQUE') ? 'log-crit' + : line.includes('esquive') ? 'log-crit' + : line.includes('HP') ? 'log-system' + : i === 0 ? 'log-player' + : 'log-monster'; return
[T{r.round}] {line}
; }) )} @@ -85,6 +90,19 @@ function CombatLog({ result }: { result: CombatResult }) { ); } +function HistoryEntry({ h }: { h: CombatLog }) { + return ( +
+ + {h.winner === 'player' ? '✓' : '✗'} {h.monster.name} + + + {h.winner === 'player' ? `+${h.xpEarned}xp +${h.goldEarned}or` : `${h.totalRounds} tours`} + +
+ ); +} + export function CombatPage() { const qc = useQueryClient(); const [selectedMonster, setSelectedMonster] = useState(null); @@ -92,7 +110,8 @@ export function CombatPage() { const [lastResult, setLastResult] = useState(null); const { data: char } = useQuery({ queryKey: ['character'], queryFn: characterApi.me }); - const endurance = (char as any)?.enduranceCurrent ?? (char as any)?.endurance ?? 0; + const endurance = char?.enduranceCurrent ?? 0; + const playerLevel = char?.level ?? 1; const canFight = endurance >= COMBAT_COST; const { data: monsters, isLoading } = useQuery({ @@ -111,11 +130,20 @@ export function CombatPage() { setLastResult(result); qc.invalidateQueries({ queryKey: ['character'] }); qc.invalidateQueries({ queryKey: ['combatHistory'] }); + qc.invalidateQueries({ queryKey: ['questsActive'] }); }, }); if (isLoading) return
Chargement des monstres…
; + // Trier : monstres appropriés en haut, trop forts en bas + const sorted = [...(monsters ?? [])].sort((a, b) => { + const aOk = a.minLevel <= playerLevel + 2 ? 0 : 1; + const bOk = b.minLevel <= playerLevel + 2 ? 0 : 1; + if (aOk !== bOk) return aOk - bOk; + return a.minLevel - b.minLevel; + }); + return (

⚔️ Combat

@@ -127,12 +155,13 @@ export function CombatPage() { Adversaire

- {monsters?.map(m => ( + {sorted.map(m => ( setSelectedMonster(m)} + playerLevel={playerLevel} /> ))}
@@ -150,7 +179,7 @@ export function CombatPage() { key={a.id} className={`card card-hover ${attackType === a.id ? 'card-gold' : ''}`} onClick={() => setAttackType(a.id)} - style={{ display: 'flex', alignItems: 'center', gap: 10 }} + style={{ display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer' }} > {a.emoji}
@@ -164,7 +193,7 @@ export function CombatPage() { {/* Coût endurance */}
Coût : {COMBAT_COST} endurance — Disponible : {endurance} - {canFight && ({Math.floor(endurance / COMBAT_COST)} combats possibles)} + {canFight && ({Math.floor(endurance / COMBAT_COST)} combats)}
{/* Bouton combattre */} @@ -192,14 +221,7 @@ export function CombatPage() { Historique récent

- {history.slice(0, 5).map(h => ( -
- - {h.winner === 'player' ? '✓' : '✗'} {(h as any).monster?.name ?? h.monsterName ?? 'Monstre'} - - +{(h as any).xpEarned ?? h.xpGained}xp +{(h as any).goldEarned ?? h.goldGained}or -
- ))} + {history.slice(0, 5).map(h => )}
)} @@ -207,7 +229,7 @@ export function CombatPage() {
{/* Résultat du dernier combat */} - {lastResult && } + {lastResult && }
); } diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx index 3c25195..a44a51a 100644 --- a/frontend/src/pages/DashboardPage.tsx +++ b/frontend/src/pages/DashboardPage.tsx @@ -150,10 +150,10 @@ export function DashboardPage() { if (isLoading) return
Chargement…
; if (isError || !char) return ; - const xpNext = (char as any).xpToNextLevel ?? Math.round(100 * Math.pow(char.level, 1.5)); - const statPoints = (char as any).statPoints ?? 0; + const xpNext = char.xpToNextLevel; + const statPoints = char.statPoints ?? 0; const needsHeal = char.hpCurrent < char.hpMax; - const endurance = (char as any).enduranceCurrent ?? (char as any).endurance ?? 0; + const endurance = char.enduranceCurrent; const REST_COST = 10; const COMBAT_COST = 5; const FORGE_COST = 10; @@ -202,9 +202,9 @@ export function DashboardPage() { Endurance - {char.enduranceCurrent ?? char.endurance} / {char.enduranceMax} + {endurance} / {char.enduranceMax}
- +
diff --git a/frontend/src/pages/ForgePage.tsx b/frontend/src/pages/ForgePage.tsx index c8655a6..afa06c5 100644 --- a/frontend/src/pages/ForgePage.tsx +++ b/frontend/src/pages/ForgePage.tsx @@ -63,7 +63,7 @@ export function ForgePage() { const [lastResult, setLastResult] = useState<{ success: boolean; newLevel: number } | null>(null); const { data: char } = useQuery({ queryKey: ['character'], queryFn: characterApi.me }); - const endurance = (char as any)?.enduranceCurrent ?? (char as any)?.endurance ?? 0; + const endurance = char?.enduranceCurrent ?? 0; const gold = char?.gold ?? 0; const { data: inventory, isLoading } = useQuery({