"use client"; import type React from "react"; import { useState, useEffect } from "react"; import Link from "next/link"; import { ChevronLeft, Sword, Shield, Zap, Heart, Brain, Eye, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; import { Progress } from "@/components/ui/progress"; import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { useToast } from "@/hooks/use-toast"; import { useUser } from "@/context/user-context"; import { enemies, type Enemy } from "@/data/enemies"; import { CombatActions } from "@/components/combat-actions"; import { EnemySelection } from "@/components/enemy-selection"; import { CombatVisualization } from "@/components/combat-visualization"; export default function CombatPage() { const { userStats, setUserStats, addExp, addItem, addGold, removeItem, inCombat: contextInCombat, setInCombat: setContextInCombat, } = useUser(); const { toast } = useToast(); // Combat states const [selectedEnemy, setSelectedEnemy] = useState(null); const [inCombat, setInCombat] = useState(false); const [playerTurn, setPlayerTurn] = useState(true); const [playerHp, setPlayerHp] = useState(userStats.hp); const [playerMp, setPlayerMp] = useState(userStats.mp); const [enemyHp, setEnemyHp] = useState(0); const [enemyMaxHp, setEnemyMaxHp] = useState(0); const [showRewards, setShowRewards] = useState(false); const [rewards, setRewards] = useState<{ exp: number; gold: number; items: any[]; }>({ exp: 0, gold: 0, items: [] }); const [isDefending, setIsDefending] = useState(false); const [skillCooldowns, setSkillCooldowns] = useState>( {} ); const [combatLog, setCombatLog] = useState([]); // Animation states const [isAttacking, setIsAttacking] = useState(false); const [isAnimating, setIsAnimating] = useState(false); const [currentDamage, setCurrentDamage] = useState(0); const [isCriticalHit, setIsCriticalHit] = useState(false); const [currentSkill, setCurrentSkill] = useState( undefined ); // Reset combat state when component unmounts useEffect(() => { return () => { if (inCombat) { // Save player HP/MP state when leaving combat setUserStats((prev) => ({ ...prev, hp: playerHp, mp: playerMp, })); } }; }, [inCombat, playerHp, playerMp, setUserStats]); // Sync local combat state with context combat state useEffect(() => { setContextInCombat(inCombat); }, [inCombat, setContextInCombat]); // Start combat with selected enemy const startCombat = (enemy: Enemy) => { setSelectedEnemy(enemy); setEnemyHp(calculateEnemyHp(enemy)); setEnemyMaxHp(calculateEnemyHp(enemy)); setPlayerHp(userStats.hp); setPlayerMp(userStats.mp); setPlayerTurn(true); setInCombat(true); setIsDefending(false); setSkillCooldowns({}); setCombatLog([]); }; // Calculate enemy HP based on level and vitality const calculateEnemyHp = (enemy: Enemy) => { return Math.floor(100 + enemy.level * 10 + enemy.stats.vit * 5); }; // Calculate damage based on attacker's strength and defender's vitality const calculateDamage = ( attackerStr: number, defenderVit: number, isCritical = false ) => { const baseDamage = Math.max( 1, Math.floor(attackerStr * 1.5 - defenderVit * 0.5) ); const randomFactor = 0.8 + Math.random() * 0.4; // 80% to 120% damage variation const criticalMultiplier = isCritical ? 1.5 : 1; return Math.floor(baseDamage * randomFactor * criticalMultiplier); }; // Check for critical hit based on agility const checkCritical = (agility: number) => { const critChance = agility * 0.5; // 0.5% per agility point return Math.random() * 100 < critChance; }; // Player attacks enemy const playerAttack = () => { if (!selectedEnemy || !inCombat || !playerTurn || isAnimating) return; const isCritical = checkCritical(userStats.stats.agi); const damage = calculateDamage( userStats.stats.str, selectedEnemy.stats.vit, isCritical ); // Set current damage for visualization setCurrentDamage(damage); setIsCriticalHit(isCritical); setCurrentSkill(undefined); setIsAnimating(true); setIsAttacking(true); addToCombatLog(`You attacked ${selectedEnemy.name} for ${damage} damage!`); }; // Handle animation completion const handleAnimationComplete = () => { setIsAnimating(false); setIsAttacking(false); if (!selectedEnemy || !inCombat) return; if (playerTurn) { // Player attack logic after animation const newEnemyHp = Math.max(0, enemyHp - currentDamage); setEnemyHp(newEnemyHp); if (newEnemyHp <= 0) { endCombat(true); return; // Important: Stop execution here to prevent enemy turn } else { setPlayerTurn(false); // Enemy's turn after a shorter delay setTimeout(() => enemyTurn(), 300); // Reduced from 1000ms to 300ms } } else { // Enemy attack logic after animation if (isDefending) { setIsDefending(false); } // Check if player is defeated if (playerHp <= 0) { endCombat(false); return; // Important: Stop execution here } setPlayerTurn(true); } }; // Player defends to reduce incoming damage const playerDefend = () => { if (!inCombat || !playerTurn || isAnimating) return; setIsAnimating(true); setIsDefending(true); addToCombatLog("You defended!"); setTimeout(() => { setIsAnimating(false); setPlayerTurn(false); // Enemy's turn after a shorter delay setTimeout(() => enemyTurn(), 300); // Reduced from 500ms to 300ms }, 600); // Reduced from 1000ms to 600ms }; // Player uses a skill const playerUseSkill = ( skillName: string, mpCost: number, cooldown: number ) => { if (!selectedEnemy || !inCombat || !playerTurn || isAnimating) return; if (playerMp < mpCost) { toast({ title: "Not enough MP", description: `You need ${mpCost} MP to use this skill.`, variant: "destructive", }); return; } if (skillCooldowns[skillName] && skillCooldowns[skillName] > 0) { toast({ title: "Skill on cooldown", description: `This skill will be available in ${skillCooldowns[skillName]} turns.`, variant: "destructive", }); return; } // Reduce MP setPlayerMp((prev) => prev - mpCost); // Set cooldown setSkillCooldowns((prev) => ({ ...prev, [skillName]: cooldown, })); let damage = 0; // Different skills have different effects switch (skillName) { case "Power Strike": damage = calculateDamage( userStats.stats.str * 2, selectedEnemy.stats.vit ); break; case "Double Slash": const hit1 = calculateDamage( userStats.stats.str * 0.7, selectedEnemy.stats.vit ); const hit2 = calculateDamage( userStats.stats.str * 0.7, selectedEnemy.stats.vit ); damage = hit1 + hit2; break; case "Fireball": damage = calculateDamage( userStats.stats.int * 2, selectedEnemy.stats.vit ); break; case "Heal": const healAmount = Math.floor(userStats.stats.int * 1.5); setPlayerHp((prev) => Math.min(userStats.maxHp, prev + healAmount)); damage = 0; break; default: damage = 0; } // Set current damage and skill for visualization setCurrentDamage(damage); setIsCriticalHit(false); setCurrentSkill(skillName); setIsAnimating(true); setIsAttacking(true); addToCombatLog(`You used ${skillName} for ${damage} damage!`); }; // Player uses an item const playerUseItem = (itemId: string) => { if (!inCombat || !playerTurn || isAnimating) return; const item = userStats.inventory.find((i) => i.id === itemId); if (!item) { toast({ title: "Item not found", description: "The selected item could not be found in your inventory.", variant: "destructive", }); return; } let effectApplied = false; // Apply item effects based on item type and id if (item.type === "Consumable") { // First check for specific item IDs for built-in items if (item.id === "item-health-potion") { const healAmount = 100; setPlayerHp((prev) => Math.min(userStats.maxHp, prev + healAmount)); toast({ title: "Health Restored", description: `You used a Health Potion and restored ${healAmount} HP.`, }); effectApplied = true; } else if (item.id === "item-mana-potion") { const manaAmount = 50; setPlayerMp((prev) => Math.min(userStats.maxMp, prev + manaAmount)); toast({ title: "Mana Restored", description: `You used a Mana Potion and restored ${manaAmount} MP.`, }); effectApplied = true; } else if (item.id === "item-greater-health-potion") { const healAmount = 200; setPlayerHp((prev) => Math.min(userStats.maxHp, prev + healAmount)); toast({ title: "Health Restored", description: `You used a Greater Health Potion and restored ${healAmount} HP.`, }); effectApplied = true; } else if (item.id === "item-greater-mana-potion") { const manaAmount = 100; setPlayerMp((prev) => Math.min(userStats.maxMp, prev + manaAmount)); toast({ title: "Mana Restored", description: `You used a Greater Mana Potion and restored ${manaAmount} MP.`, }); effectApplied = true; } else if (item.id === "item-healing-elixir") { const healAmount = 350; setPlayerHp((prev) => Math.min(userStats.maxHp, prev + healAmount)); toast({ title: "Health Restored", description: `You used a Healing Elixir and restored ${healAmount} HP.`, }); effectApplied = true; } else if (item.id === "item-mana-elixir") { const manaAmount = 175; setPlayerMp((prev) => Math.min(userStats.maxMp, prev + manaAmount)); toast({ title: "Mana Restored", description: `You used a Mana Elixir and restored ${manaAmount} MP.`, }); effectApplied = true; } // For other consumables, check name patterns else if ( item.name.toLowerCase().includes("health") || item.name.toLowerCase().includes("healing") || item.name.toLowerCase().includes("hp") ) { // Default healing amount (can be customized based on rarity) let healAmount = 50; if (item.rarity === "Uncommon") healAmount = 100; if (item.rarity === "Rare") healAmount = 200; if (item.rarity === "Epic") healAmount = 350; if (item.rarity === "Legendary") healAmount = 500; setPlayerHp((prev) => Math.min(userStats.maxHp, prev + healAmount)); toast({ title: "Health Restored", description: `You used ${item.name} and restored ${healAmount} HP.`, }); effectApplied = true; } else if ( item.name.toLowerCase().includes("mana") || item.name.toLowerCase().includes("mp") ) { // Default mana restoration amount let manaAmount = 25; if (item.rarity === "Uncommon") manaAmount = 50; if (item.rarity === "Rare") manaAmount = 100; if (item.rarity === "Epic") manaAmount = 175; if (item.rarity === "Legendary") manaAmount = 250; setPlayerMp((prev) => Math.min(userStats.maxMp, prev + manaAmount)); toast({ title: "Mana Restored", description: `You used ${item.name} and restored ${manaAmount} MP.`, }); effectApplied = true; } else { toast({ title: "Item Effect Unknown", description: "This item's effect is not implemented yet.", variant: "destructive", }); } } else { toast({ title: "Cannot Use Item", description: "Only consumable items can be used in combat.", variant: "destructive", }); return; } if (effectApplied) { // Remove the item from inventory removeItem(itemId, 1); // End player's turn setPlayerTurn(false); setTimeout(() => enemyTurn(), 300); // Reduced from 1000ms to 300ms } }; // Enemy takes their turn const enemyTurn = () => { if (!selectedEnemy || !inCombat) return; // Reduce cooldowns setSkillCooldowns((prev) => { const newCooldowns = { ...prev }; Object.keys(newCooldowns).forEach((skill) => { if (newCooldowns[skill] > 0) { newCooldowns[skill] -= 1; } }); return newCooldowns; }); // Enemy decides what to do (for now, just basic attack) const damage = calculateDamage( selectedEnemy.stats.str, userStats.stats.vit ); // Apply defense reduction if player is defending const actualDamage = isDefending ? Math.floor(damage * 0.5) : damage; // Set current damage for visualization setCurrentDamage(actualDamage); setIsCriticalHit(false); setCurrentSkill(undefined); setIsAnimating(true); setIsAttacking(true); // Update player HP const newPlayerHp = Math.max(0, playerHp - actualDamage); setPlayerHp(newPlayerHp); addToCombatLog( `${selectedEnemy.name} attacked you for ${actualDamage} damage!` ); // Check if player is defeated - this will be handled in handleAnimationComplete }; // Add message to combat log const addToCombatLog = (message: string) => { setCombatLog((prev) => [...prev, message]); }; // End combat and determine rewards or penalties const endCombat = (victory: boolean) => { if (!selectedEnemy) return; if (victory) { addToCombatLog(`You have defeated ${selectedEnemy.name}!`); // Set rewards const combatRewards = { exp: selectedEnemy.rewards.exp, gold: selectedEnemy.rewards.gold, items: selectedEnemy.rewards.items, }; setRewards(combatRewards); setShowRewards(true); toast({ title: "Victory!", description: `You have defeated ${selectedEnemy.name}!`, }); } else { addToCombatLog(`You have been defeated by ${selectedEnemy.name}.`); // Apply defeat penalties (lose some gold, etc.) const goldLoss = Math.floor(userStats.gold * 0.1); // Lose 10% of gold setUserStats((prev) => ({ ...prev, hp: Math.max(1, Math.floor(prev.maxHp * 0.1)), // Restore to 10% HP gold: Math.max(0, prev.gold - goldLoss), })); addToCombatLog( `You lost ${goldLoss} gold and barely escaped with your life.` ); } setInCombat(false); setIsAnimating(false); setIsAttacking(false); }; // Claim rewards after victory const claimRewards = () => { if (!selectedEnemy) return; // Add experience addExp(rewards.exp); // Add gold addGold(rewards.gold); // Add items to inventory rewards.items.forEach((item) => { addItem(item); }); // Show toast notification toast({ title: "Rewards Claimed", description: `You gained ${rewards.exp} EXP, ${rewards.gold} Gold, and ${rewards.items.length} items.`, }); // Reset combat setShowRewards(false); setSelectedEnemy(null); }; // Skip turn (for testing) const skipTurn = () => { if (!inCombat) return; if (playerTurn) { setPlayerTurn(false); setTimeout(() => enemyTurn(), 500); } else { setPlayerTurn(true); } }; // Flee from combat const fleeCombat = () => { if (!inCombat || !selectedEnemy || isAnimating) return; // 50% chance to successfully flee based on agility difference const fleeChance = 50 + (userStats.stats.agi - selectedEnemy.stats.agi) * 2; if (Math.random() * 100 < fleeChance) { toast({ title: "Escaped", description: "You successfully fled from combat!", }); setInCombat(false); setSelectedEnemy(null); } else { toast({ title: "Failed to Escape", description: "You failed to flee!", }); setPlayerTurn(false); setTimeout(() => enemyTurn(), 300); // Reduced from 1000ms to 300ms } }; return (
{/* Header */}

Combat Arena

Gold: {userStats.gold}
{/* Recovery notification card */}

Automatic Recovery System

Your character naturally recovers 10% of maximum HP and MP every 5 minutes when not in combat. This recovery continues even when you're offline, allowing you to return stronger after a break.

{/* Main Combat Area */}
{/* Left Column - Combat Area */}
{!selectedEnemy && !inCombat ? ( ) : ( <> {/* Combat Visualization */} {inCombat && selectedEnemy && ( )} {!inCombat && selectedEnemy && (
{selectedEnemy.name} Level {selectedEnemy.level}
{selectedEnemy.description}
{/* Enemy Stats */}
STR {selectedEnemy.stats.str}
VIT {selectedEnemy.stats.vit}
AGI {selectedEnemy.stats.agi}
INT {selectedEnemy.stats.int}
PER {selectedEnemy.stats.per}
{!inCombat && !showRewards && ( )} {showRewards && ( )}
)} )}
{/* Right Column - Player Stats (hidden during combat) */} {!inCombat && (
Your Stats Level {userStats.level} Hunter {/* HP/MP Bars */}
HP {playerHp}/{userStats.maxHp}
MP {playerMp}/{userStats.maxMp}
{/* Stats */}
STR: {userStats.stats.str}
VIT: {userStats.stats.vit}
AGI: {userStats.stats.agi}
INT: {userStats.stats.int}
PER: {userStats.stats.per}
)}
{/* Combat Actions */} {inCombat && playerTurn && (
)} {/* Rewards Dialog */} Victory Rewards You have defeated {selectedEnemy?.name}!
Experience: {rewards.exp} EXP
Gold: {rewards.gold} Gold

Items:

{rewards.items.map((item, index) => (
{item.name}
{item.type}
))}
); }