import { useTranslation } from 'react-i18next' import type { CombatState, CombatLogEntry, Profile, Equipment, PlayerState } from './types' import { getTranslatedText } from '../../utils/i18nUtils' interface CombatViewProps { combatState: CombatState combatLog: CombatLogEntry[] profile: Profile | null playerState: PlayerState | null equipment: Equipment enemyName: string enemyImage: string enemyTurnMessage: string pvpTimeRemaining: number | null turnTimeRemaining: number | null onCombatAction: (action: string) => void onFlee: () => void onPvPAction: (action: string) => void onExitCombat: () => void onExitPvPCombat: () => void flashEnemy?: boolean buttonsDisabled?: boolean floatingTexts?: { id: number, text: string, x: number, y: number, type: 'damage-player' | 'damage-enemy' | 'damage-player-dealt' | 'heal' }[] } function CombatView({ combatState, combatLog, profile: _profile, playerState, enemyName, enemyImage, enemyTurnMessage, pvpTimeRemaining, turnTimeRemaining, onCombatAction, onPvPAction, onExitCombat, onExitPvPCombat, flashEnemy, buttonsDisabled, floatingTexts = [] }: CombatViewProps) { const { t } = useTranslation() const displayEnemyName = typeof enemyName === 'object' ? getTranslatedText(enemyName) : (enemyName || 'Enemy') // Render structured combat messages const renderCombatMessage = (msg: any) => { // Support both old string format and new structured format if (typeof msg === 'string') { return msg // Legacy format } if (!msg || !msg.type) { return String(msg) } const { type, data } = msg switch (type) { case 'combat_start': return t('combat.messages.combat_start', { enemy: getTranslatedText(data.npc_name) }) case 'player_attack': return t('combat.messages.player_attack', { damage: data.damage }) case 'enemy_attack': return t('combat.messages.enemy_attack', { enemy: getTranslatedText(data.npc_name), damage: data.damage }) case 'victory': return t('combat.messages.victory', { enemy: getTranslatedText(data.npc_name) }) case 'flee_fail': return t('combat.messages.flee_fail', { enemy: getTranslatedText(data.npc_name), damage: data.damage }) default: return JSON.stringify(msg) } } return (

{combatState.is_pvp ? `⚔️ ${t('combat.title')} - PvP` : `⚔️ ${t('combat.title')} - ${displayEnemyName}`}

{combatState.is_pvp ? ( /* PvP Combat UI - Unified Layout */
{/* Opponent Display (using same structure as PvE Enemy) */}
{floatingTexts.map(ft => (
{ft.text}
))}
{(() => { if (!combatState.pvp_combat) return null const opponent = combatState.pvp_combat.is_attacker ? combatState.pvp_combat.defender : combatState.pvp_combat.attacker if (!opponent) return
// Use a default avatar if no image, or maybe the class image if available? // For now, let's use a placeholder or try to get it from profile if passed? // The opponent object has: username, level, hp, max_hp. // It might not have an image url. return (
👤
{opponent.username} (Lv. {opponent.level})
) })()}
{/* Opponent HP Bar */} {(() => { if (!combatState.pvp_combat) return null const opponent = combatState.pvp_combat.is_attacker ? combatState.pvp_combat.defender : combatState.pvp_combat.attacker if (!opponent) return null return (
{opponent.username}: {opponent.hp} / {opponent.max_hp}
) })()} {/* Player HP Bar */} {(() => { if (!combatState.pvp_combat) return null const you = combatState.pvp_combat.is_attacker ? combatState.pvp_combat.attacker : combatState.pvp_combat.defender if (!you) return null return (
You: {you.hp} / {you.max_hp}
) })()}
{combatState.pvp_combat.combat_over ? ( {combatState.pvp_combat.attacker_fled || combatState.pvp_combat.defender_fled ? "🏃 Combat Ended" : "💀 Combat Over"} ) : combatState.pvp_combat.your_turn ? ( ✅ Your Turn ({pvpTimeRemaining ?? combatState.pvp_combat.time_remaining}s) ) : ( ⏳ Opponent's Turn ({pvpTimeRemaining ?? combatState.pvp_combat.time_remaining}s) )}
{!combatState.pvp_combat.combat_over ? ( <> ) : ( )}
{/* Combat Log */}

{t('combat.combatLog')}

{combatLog.length > 0 ? ( combatLog.map((entry: any) => (
[{entry.time}] {renderCombatMessage(entry.message)}
)) ) : (
PvP Combat started...
)}
) : ( /* PvE Combat UI */ <>
{/* Intent Bubble - Moved here to avoid overflow:hidden clipping */} {combatState.combat?.npc_intent && !combatState.combat_over && (
{combatState.combat.npc_intent === 'attack' ? '⚔️' : combatState.combat.npc_intent === 'defend' ? '🛡️' : combatState.combat.npc_intent === 'special' ? '🔥' : '❓'} {combatState.combat.npc_intent}
)}
{floatingTexts.map(ft => (
{ft.text}
))}
{enemyName
{t('combat.enemyHp')}: {combatState.combat?.npc_hp || 0} / {combatState.combat?.npc_max_hp || 100}
{playerState && (
{t('combat.playerHp')}: {playerState.health} / {playerState.max_health}
)}
{!combatState.combat_over ? ( enemyTurnMessage ? ( 🗡️ Enemy's turn... ) : combatState.combat?.turn === 'player' ? ( <> ✅ {t('combat.yourTurn')} {turnTimeRemaining !== null && ( ⏱️ {Math.floor(turnTimeRemaining / 60)}:{String(Math.floor(turnTimeRemaining % 60)).padStart(2, '0')} )} ) : ( ⚠️ {t('combat.enemyTurn')} ) ) : ( {combatState.player_won ? `✅ ${t('combat.victory')}` : combatState.player_fled ? `🏃 ${t('combat.fleeSuccess')}` : `💀 ${t('combat.defeat')}`} )}
{/* PvE Combat Actions */}
{!combatState.combat_over ? ( <> ) : ( )}
{/* Combat Log */}

{t('combat.combatLog')}

{combatLog.length > 0 ? ( combatLog.map((entry: any) => (
[{entry.time}] {renderCombatMessage(entry.message)}
)) ) : (
Combat started...
)}
)}
) } export default CombatView