import React, { useState, useEffect } from 'react'; import { Briefcase, Coffee, Printer, Bug, Mail, FileText, Presentation, Monitor, Phone, Calendar } from 'lucide-react'; const OfficeDnD = () => { // Character Classes const CHARACTER_CLASSES = { IT_WIZARD: { name: 'IT Wizard', icon: <Monitor className="text-blue-500" />, baseStats: { hp: 30, mp: 20, atk: 6, def: 4 }, spells: [ { name: 'Tech Support', cost: 3, damage: 8, type: 'heal', description: 'Restore HP with technical expertise' }, { name: 'System Crash', cost: 5, damage: 12, type: 'damage', description: 'Unleash a devastating system failure' } ] }, SALES_WARRIOR: { name: 'Sales Warrior', icon: <Phone className="text-red-500" />, baseStats: { hp: 40, mp: 10, atk: 8, def: 6 }, spells: [ { name: 'Cold Call Strike', cost: 4, damage: 10, type: 'damage', description: 'Launch a surprise sales pitch attack' }, { name: 'Commission Boost', cost: 3, damage: 0, type: 'buff', description: 'Increase attack power temporarily' } ] }, HR_CLERIC: { name: 'HR Cleric', icon: <Calendar className="text-green-500" />, baseStats: { hp: 35, mp: 15, atk: 5, def: 7 }, spells: [ { name: 'Team Building', cost: 4, damage: 10, type: 'heal', description: 'Heal the party through group activities' }, { name: 'Performance Review', cost: 6, damage: 15, type: 'damage', description: 'Deliver a crushing evaluation' } ] } }; // Monsters const MONSTERS = { PRINTER_DRAGON: { name: 'Printer Dragon', icon: <Printer size={32} className="text-red-600" />, stats: { hp: 30, atk: 7, def: 5 }, attacks: [ { name: 'Paper Jam', damage: 8, description: 'Unleashes a storm of crumpled papers' }, { name: 'Toner Breath', damage: 12, description: 'Sprays toxic toner in all directions' } ] }, CODE_BUGBEAR: { name: 'Code Bugbear', icon: <Bug size={32} className="text-purple-600" />, stats: { hp: 20, atk: 6, def: 3 }, attacks: [ { name: 'Syntax Error', damage: 6, description: 'Causes confusion with broken code' }, { name: 'Infinite Loop', damage: 10, description: 'Traps the target in an endless cycle' } ] }, DEADLINE_DEMON: { name: 'Deadline Demon', icon: <Calendar size={32} className="text-gray-800" />, stats: { hp: 25, atk: 8, def: 4 }, attacks: [ { name: 'Urgent Meeting', damage: 7, description: 'Summons an unavoidable meeting' }, { name: 'Overtime Drain', damage: 11, description: 'Saps energy with extended hours' } ] } }; // Items const ITEMS = { COFFEE: { name: 'Magic Coffee', icon: <Coffee className="text-brown-600" />, effect: { type: 'heal', amount: 15 }, description: 'Restores HP and temporary attack boost' }, STAPLER: { name: 'Stapler of Binding', icon: <FileText className="text-gray-600" />, effect: { type: 'weapon', amount: 5 }, description: '+5 Attack Power' }, POWERPOINT: { name: 'PowerPoint of Persuasion', icon: <Presentation className="text-blue-600" />, effect: { type: 'spell', amount: 8 }, description: 'Deals 8 damage to all enemies' } }; // Game State const [gameState, setGameState] = useState('characterSelect'); // characterSelect, exploring, combat, dialog const [player, setPlayer] = useState(null); const [currentRoom, setCurrentRoom] = useState(0); const [inventory, setInventory] = useState([]); const [combat, setCombat] = useState(null); const [messages, setMessages] = useState([]); const [currentDialog, setCurrentDialog] = useState(null); // Character Selection Component const CharacterSelect = () => ( <div className="flex flex-col items-center p-8 bg-white rounded-lg shadow-lg"> <h2 className="text-3xl font-bold mb-6">Choose Your Office Class</h2> <div className="grid grid-cols-3 gap-6"> {Object.entries(CHARACTER_CLASSES).map(([key, charClass]) => ( <div key={key} className="p-4 border rounded-lg hover:border-blue-500 cursor-pointer transition-all" onClick={() => selectCharacter(charClass)} > <div className="flex justify-center mb-4">{charClass.icon}</div> <h3 className="text-xl font-bold mb-2">{charClass.name}</h3> <div className="text-sm"> <p>HP: {charClass.baseStats.hp}</p> <p>MP: {charClass.baseStats.mp}</p> <p>Attack: {charClass.baseStats.atk}</p> <p>Defense: {charClass.baseStats.def}</p> </div> <div className="mt-4 text-sm"> <p className="font-bold">Special Abilities:</p> {charClass.spells.map(spell => ( <p key={spell.name} className="text-gray-600">{spell.name}</p> ))} </div> </div> ))} </div> </div> ); // Game Over Component const GameOverScreen = () => ( <div className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center"> <div className="bg-white p-8 rounded-lg text-center"> <h2 className="text-3xl font-bold mb-4 text-red-600">Game Over</h2> <p className="mb-6">You have been defeated in the corporate dungeon...</p> <button className="px-6 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" onClick={() => { // Reset player to full health and return to character select setGameState('characterSelect'); setPlayer(null); setCombat(null); setMessages([]); }} > Try Again </button> </div> </div> ); // Combat Component const CombatScreen = () => { const [selectedAction, setSelectedAction] = useState(null); const performAction = (action) => { if (!combat) return; let newMessages = [...messages]; let newCombat = { ...combat }; switch (action.type) { case 'attack': const damage = Math.max(1, player.stats.atk - combat.enemy.stats.def); newCombat.enemy.stats.hp -= damage; newMessages.push(`You attack ${combat.enemy.name} for ${damage} damage!`); break; case 'spell': if (player.stats.mp >= action.cost) { const spellDamage = action.damage; newCombat.enemy.stats.hp -= spellDamage; player.stats.mp -= action.cost; newMessages.push(`You cast ${action.name} for ${spellDamage} damage!`); } else { newMessages.push("Not enough MP!"); return; } break; case 'item': // Item usage logic break; } // Enemy turn if (newCombat.enemy.stats.hp > 0) { const enemyAttack = combat.enemy.attacks[Math.floor(Math.random() * combat.enemy.attacks.length)]; const enemyDamage = Math.max(1, enemyAttack.damage - player.stats.def); const newHp = Math.max(0, player.stats.hp - enemyDamage); // Prevent negative HP setPlayer(prev => ({ ...prev, stats: { ...prev.stats, hp: newHp } })); newMessages.push(`${combat.enemy.name} uses ${enemyAttack.name} for ${enemyDamage} damage!`); // Check if player died from this attack if (newHp <= 0) { endCombat('lose'); return; } } setMessages(newMessages); setCombat(newCombat); // Check win/lose conditions if (newCombat.enemy.stats.hp <= 0) { endCombat('win'); } else if (player.stats.hp <= 0) { endCombat('lose'); } }; return ( <div className="grid grid-cols-2 gap-4 p-4 bg-gray-100 rounded-lg"> <div className="p-4 bg-white rounded-lg"> <h3 className="text-xl font-bold mb-4">Player</h3> <div className="flex items-center space-x-4"> {player?.class.icon} <div> <p>HP: {player?.stats.hp} / {player?.class.baseStats.hp}</p> <p>MP: {player?.stats.mp} / {player?.class.baseStats.mp}</p> </div> </div> <div className="mt-4"> <h4 className="font-bold mb-2">Actions:</h4> <div className="grid grid-cols-2 gap-2"> <button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" onClick={() => performAction({ type: 'attack' })} > Attack </button> {player?.class.spells.map(spell => ( <button key={spell.name} className="px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600" onClick={() => performAction({ type: 'spell', ...spell })} > {spell.name} ({spell.cost} MP) </button> ))} </div> </div> </div> <div className="p-4 bg-white rounded-lg"> <h3 className="text-xl font-bold mb-4">{combat?.enemy.name}</h3> <div className="flex items-center space-x-4"> {combat?.enemy.icon} <div> <p>HP: {combat?.enemy.stats.hp}</p> <p>Attacks: {combat?.enemy.attacks.map(a => a.name).join(', ')}</p> </div> </div> </div> <div className="col-span-2 p-4 bg-white rounded-lg"> <h4 className="font-bold mb-2">Combat Log:</h4> <div className="h-32 overflow-y-auto"> {messages.slice(-5).map((msg, i) => ( <p key={i} className="text-sm mb-1">{msg}</p> ))} </div> </div> </div> ); }; // Exploration Component const ExplorationScreen = () => { const rooms = [ { name: 'Reception Hall', description: 'The entrance to the corporate dungeon' }, { name: 'Cubicle Maze', description: 'A labyrinth of office cubicles' }, { name: 'Break Room', description: 'A sacred place of rest and coffee' }, { name: 'Server Room', description: 'The lair of the IT department' }, { name: 'Conference Room B', description: 'An ancient chamber of meetings' } ]; return ( <div className="p-4 bg-white rounded-lg"> <h2 className="text-2xl font-bold mb-4">{rooms[currentRoom].name}</h2> <p className="mb-4">{rooms[currentRoom].description}</p> <div className="grid grid-cols-2 gap-4"> {currentRoom < rooms.length - 1 && ( <button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" onClick={() => setCurrentRoom(currentRoom + 1)} > Proceed Deeper </button> )} <button className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600" onClick={() => startRandomEncounter()} > Search Area </button> </div> </div> ); }; // Game Logic Functions const selectCharacter = (charClass) => { setPlayer({ class: charClass, stats: { ...charClass.baseStats }, level: 1, exp: 0 }); setGameState('exploring'); }; const startRandomEncounter = () => { const randomMonster = Object.values(MONSTERS)[Math.floor(Math.random() * Object.values(MONSTERS).length)]; setCombat({ enemy: { ...randomMonster, stats: { ...randomMonster.stats } } }); setGameState('combat'); setMessages([`A wild ${randomMonster.name} appears!`]); }; const endCombat = (result) => { if (result === 'win') { setMessages([...messages, 'Victory! You defeated the enemy!']); // Add rewards and experience const rewardExp = Math.floor(Math.random() * 20) + 10; setPlayer(prev => ({ ...prev, exp: prev.exp + rewardExp, stats: { ...prev.stats, hp: Math.min(prev.stats.hp + 5, prev.class.baseStats.hp) // Heal a bit after victory } })); setMessages(prev => [...prev, `Gained ${rewardExp} experience!`]); } else { setMessages([...messages, 'You were defeated...']); setGameState('gameOver'); return; // Don't continue with the timeout } setTimeout(() => { setCombat(null); setGameState('exploring'); }, 2000); }; // Main Render return ( <div className="max-w-4xl mx-auto p-4"> <h1 className="text-4xl font-bold text-center mb-6">Office D&D Adventure</h1> {gameState === 'characterSelect' && <CharacterSelect />} {gameState === 'exploring' && <ExplorationScreen />} {gameState === 'combat' && <CombatScreen />} {gameState === 'gameOver' && <GameOverScreen />} {player && gameState !== 'characterSelect' && ( <div className="mt-4 p-4 bg-white rounded-lg shadow"> <h3 className="font-bold mb-2">Character Status:</h3> <div className="flex items-center space-x-4"> <div>{player.class.icon}</div> <div> <p>Class: {player.class.name}</p> <p>Level: {player.level}</p> <p>HP: {player.stats.hp}/{player.class.baseStats.hp}</p> <p>MP: {player.stats.mp}/{player.class.baseStats.mp}</p> </div> </div> </div> )} </div> ); }; export default OfficeDnD;