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;