Game-Server/client/src/views/GameInterface/components/DungeonScreen.jsx
2026-04-04 20:04:45 +03:00

219 lines
7.0 KiB
JavaScript

import React, { useState, useEffect, useRef } from "react";
import GameDataManager from "../../../services/GameDataManager.js";
import "./DungeonScreen.css";
const DungeonScreen = ({ session, socket }) => {
const [roomData, setRoomData] = useState(session.room);
const [hostiles, setHostiles] = useState(session.hostiles || []);
const [roomIndex, setRoomIndex] = useState(session.roomIndex);
const [enemyHp, setEnemyHp] = useState(null);
const [isEnemyDefeated, setIsEnemyDefeated] = useState(false);
const [isLooted, setIsLooted] = useState(false);
const [log, setLog] = useState([
"SYSTEM: Neural link established. Scanning sector...",
]);
const logEndRef = useRef(null);
const currentEnemy = hostiles.length > 0 ? hostiles[0] : null;
const dungeonData = GameDataManager.getDungeon(session.dungeonId);
const getEnemyDisplayName = (enemy) => {
if (!enemy) return "UNKNOWN_ENTITY";
const data = GameDataManager.getEnemy(enemy.id);
return data?.displayName || enemy.displayName || enemy.id;
};
const getRoomDisplayName = (room) => {
if (!room) return "UNKNOWN_LOCATION";
const data = GameDataManager.getRoom(room.id);
return data?.displayName || room.displayName || room.id;
};
useEffect(() => {
logEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [log]);
useEffect(() => {
socket.on("dungeon:room_update", (data) => {
setRoomData(data.room);
setHostiles(data.hostiles || []);
setRoomIndex(data.roomIndex);
setIsEnemyDefeated(false);
setIsLooted(false);
setEnemyHp(null);
addLog(`--- ENTERING SECTOR ${data.roomIndex + 1} ---`);
});
socket.on("dungeon:combat_result", (data) => {
if (data.message) addLog(data.message);
if (data.enemyHp !== undefined) {
const maxHp = currentEnemy?.stats?.health || 100;
const hpPercent = (data.enemyHp / maxHp) * 100;
setEnemyHp(hpPercent);
}
if (data.targetDefeated) {
setIsEnemyDefeated(true);
addLog("TARGET_NEUTRALIZED: Threat eliminated.");
}
});
return () => {
socket.off("dungeon:room_update");
socket.off("dungeon:combat_result");
};
}, [socket, currentEnemy]);
const addLog = (text) => {
const time = new Date().toLocaleTimeString([], {
hour12: false,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
setLog((prev) => [...prev, `[${time}] ${text}`]);
};
const handleCombat = () => {
if (isEnemyDefeated || !currentEnemy) return;
socket.emit("dungeon:combat_step", { enemyId: currentEnemy.id });
addLog(`Initiating strike sequence...`);
};
const handleLoot = () => {
setIsLooted(true);
addLog("Loot encryption bypassed. Resources transferred.");
};
const handleNextRoom = () => {
socket.emit("dungeon:next_room");
};
return (
<div className="dungeon-active-screen">
<div className="dungeon-header">
<div className="room-progress">
<div className="progress-text">
SECTOR {roomIndex + 1} / {session.totalRooms}
</div>
<div className="progress-bar">
<div
className="fill"
style={{
width: `${((roomIndex + 1) / session.totalRooms) * 100}%`,
}}
></div>
</div>
</div>
<div className="dungeon-title-area">
<div className="dungeon-name">
{dungeonData?.displayName || "MISSION_ACTIVE"}
</div>
<div className="dungeon-status-tag">LIVE_FEED</div>
</div>
</div>
<div className="environment-panel">
<div className="env-header">ENVIRONMENT_SCAN</div>
<div className="env-info">
<div className="env-icon">
<i
className={`fas ${
roomData?.id?.includes("boss")
? "fa-skull"
: roomData?.id?.includes("loot")
? "fa-box-open"
: "fa-microchip"
}`}
></i>
</div>
<div className="env-details">
<div className="env-id">{getRoomDisplayName(roomData)}</div>
<div className="env-type">
TYPE: {hostiles.length > 0 ? "COMBAT_ZONE" : "SECURE_AREA"}
</div>
</div>
</div>
</div>
<div className="battle-layout">
<div
className={`enemy-display ${isEnemyDefeated ? "target-lost" : ""}`}
>
{currentEnemy ? (
<div className={`enemy-card ${isEnemyDefeated ? "defeated" : ""}`}>
<div className="threat-tag">
{isEnemyDefeated ? "SIGNAL_LOST" : "HOSTILE_DETECTED"}
</div>
<div className="enemy-icon">
<i
className={`fas ${isEnemyDefeated ? "fa-skull-crossbones" : "fa-robot"}`}
></i>
</div>
<h3 className="enemy-name">
{getEnemyDisplayName(currentEnemy)}
</h3>
<div className="enemy-hp-container">
<div className="hp-label">STRUCTURE INTEGRITY</div>
<div className="hp-bar-mini">
<div
className="hp-fill-mini"
style={{
width: isEnemyDefeated
? "0%"
: `${enemyHp !== null ? enemyHp : 100}%`,
}}
></div>
</div>
</div>
<div className="enemy-info-footer">
<span>LVL: {currentEnemy.level || 1}</span>
<span className="card-id">{currentEnemy.id}</span>
</div>
</div>
) : (
<div className="empty-room">
<i className="fas fa-satellite-dish"></i>
<p>NO HOSTILES IN RANGE</p>
</div>
)}
</div>
<div className="combat-log-wrapper">
<div className="log-header">COMBAT_LOG_V3.0</div>
<div className="combat-log custom-scroll">
{log.map((entry, i) => (
<div key={i} className="log-entry">
<span className="log-arrow">&gt;</span> {entry}
</div>
))}
<div ref={logEndRef} />
</div>
</div>
</div>
<div className="dungeon-controls">
{!isEnemyDefeated && currentEnemy && (
<button className="ctrl-btn combat" onClick={handleCombat}>
<i className="fas fa-bolt"></i> ENGAGE
</button>
)}
{isEnemyDefeated && !isLooted && (
<button className="ctrl-btn loot" onClick={handleLoot}>
<i className="fas fa-download"></i> COLLECT_ASSETS
</button>
)}
{(isLooted || !currentEnemy) && (
<button className="ctrl-btn next" onClick={handleNextRoom}>
PROCEED <i className="fas fa-chevron-right"></i>
</button>
)}
</div>
</div>
);
};
export default DungeonScreen;