diff --git a/client/src/services/GameDataManager.js b/client/src/services/GameDataManager.js
index 141d527..9f3a987 100644
--- a/client/src/services/GameDataManager.js
+++ b/client/src/services/GameDataManager.js
@@ -1,5 +1,3 @@
-import React from "react";
-
class GameDataManager {
constructor() {
this.items = new Map();
@@ -7,6 +5,7 @@ class GameDataManager {
this.skills = new Map();
this.dungeons = new Map();
this.hostiles = new Map();
+ this.rooms = new Map();
this.translations = {};
this.manifest = {};
this.currentLang = localStorage.getItem("selected_lang") || "en_US";
@@ -29,6 +28,9 @@ class GameDataManager {
if (Array.isArray(data.skills)) {
data.skills.forEach((s) => this.skills.set(s.id, s));
}
+ if (Array.isArray(data.rooms)) {
+ data.rooms.forEach((r) => this.rooms.set(r.id, r));
+ }
if (data.languages) {
this.translations = data.languages;
@@ -122,6 +124,10 @@ class GameDataManager {
};
}
+ getEnemy(id) {
+ return this.getHostile(id);
+ }
+
getItem(id) {
const item = this.items.get(id);
@@ -176,6 +182,16 @@ class GameDataManager {
};
}
+ getRoom(id) {
+ const room = this.rooms.get(id);
+ if (!room) return null;
+ return {
+ ...room,
+ displayName: this.t(room.displayName),
+ description: this.t(room.description),
+ };
+ }
+
setLanguage(langCode) {
if (this.translations[langCode]) {
this.currentLang = langCode;
diff --git a/client/src/views/GameInterface/components/DungeonScreen.css b/client/src/views/GameInterface/components/DungeonScreen.css
index bcc2348..8dc10cd 100644
--- a/client/src/views/GameInterface/components/DungeonScreen.css
+++ b/client/src/views/GameInterface/components/DungeonScreen.css
@@ -8,9 +8,9 @@
font-family: "Space Mono", monospace;
color: #e0e6ed;
box-sizing: border-box;
+ overflow: hidden;
}
-/* HEADER */
.dungeon-header {
display: flex;
justify-content: space-between;
@@ -18,6 +18,7 @@
border-bottom: 1px solid rgba(0, 212, 255, 0.3);
padding-bottom: 15px;
position: relative;
+ flex-shrink: 0;
}
.dungeon-header::after {
@@ -73,19 +74,18 @@
animation: blink 1.5s infinite;
}
-/* LAYOUT */
.battle-layout {
display: grid;
grid-template-columns: 1.2fr 1fr;
flex: 1;
gap: 30px;
- min-height: 0; /* Важливо для overflow лога */
+ min-height: 0;
}
-/* ENEMY CARD */
.enemy-display {
position: relative;
height: 100%;
+ min-height: 0;
}
.enemy-card {
@@ -152,7 +152,6 @@
text-transform: uppercase;
}
-/* HP BAR MINI */
.enemy-hp-container {
width: 60%;
margin-bottom: 30px;
@@ -187,12 +186,14 @@
color: rgba(160, 172, 186, 0.7);
}
-/* COMBAT LOG */
.combat-log-wrapper {
display: flex;
flex-direction: column;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(26, 38, 56, 0.8);
+ min-height: 0;
+ max-height: 100%;
+ overflow: hidden;
}
.log-header {
@@ -203,6 +204,7 @@
letter-spacing: 1px;
color: #00d4ff;
border-bottom: 1px solid rgba(26, 38, 56, 0.8);
+ flex-shrink: 0;
}
.combat-log {
@@ -215,12 +217,14 @@
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #1a2638 transparent;
+ min-height: 0;
}
.log-entry {
line-height: 1.4;
color: #a0acba;
animation: slideIn 0.2s ease-out;
+ word-break: break-all;
}
.log-arrow {
@@ -229,11 +233,11 @@
font-weight: bold;
}
-/* CONTROLS */
.dungeon-controls {
display: flex;
gap: 20px;
height: 70px;
+ flex-shrink: 0;
}
.ctrl-btn {
@@ -277,7 +281,6 @@
transform: scale(0.98);
}
-/* UTILS */
.empty-room {
height: 100%;
display: flex;
@@ -325,19 +328,17 @@
border-radius: 2px;
}
-/* Вирівнювання футера картки */
.enemy-info-footer {
display: flex;
- justify-content: space-between; /* Розносимо LVL та ID по боках */
- width: 80%; /* Щоб не були впритул до країв */
- margin-top: auto; /* Притискаємо до низу */
+ justify-content: space-between;
+ width: 80%;
+ margin-top: auto;
padding-bottom: 20px;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 1px;
}
-/* Ефект трясіння при атаці */
.enemy-card.taking-damage {
animation: shake 0.2s ease-in-out;
border-color: #ffffff;
@@ -362,7 +363,60 @@
}
}
-/* Покращення читабельності лога */
.combat-log-wrapper {
- min-width: 300px; /* Щоб лог не стискався занадто сильно */
+ min-width: 300px;
+}
+
+.environment-panel {
+ background: rgba(0, 20, 40, 0.6);
+ border: 1px solid rgba(0, 255, 255, 0.2);
+ margin-bottom: 15px;
+ padding: 12px;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ flex-shrink: 0;
+}
+
+.env-header {
+ font-size: 0.65rem;
+ color: #00ffff;
+ opacity: 0.7;
+ letter-spacing: 1.5px;
+ margin-bottom: 8px;
+}
+
+.env-info {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.env-icon {
+ width: 36px;
+ height: 36px;
+ background: rgba(0, 255, 255, 0.1);
+ border: 1px solid rgba(0, 255, 255, 0.2);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #00ffff;
+ font-size: 1.1rem;
+}
+
+.env-details .env-id {
+ font-family: "Orbitron", sans-serif;
+ font-size: 0.8rem;
+ color: #fff;
+}
+
+.env-details .env-type {
+ font-size: 0.6rem;
+ color: #888;
+ margin-top: 2px;
+}
+
+.card-id {
+ opacity: 0.4;
+ font-size: 0.6rem;
}
diff --git a/client/src/views/GameInterface/components/DungeonScreen.jsx b/client/src/views/GameInterface/components/DungeonScreen.jsx
index c5a3439..cc9600f 100644
--- a/client/src/views/GameInterface/components/DungeonScreen.jsx
+++ b/client/src/views/GameInterface/components/DungeonScreen.jsx
@@ -4,6 +4,7 @@ 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);
@@ -14,31 +15,43 @@ const DungeonScreen = ({ session, socket }) => {
const logEndRef = useRef(null);
- // Отримуємо дані про ворога та данж через GameDataManager
- const rawEnemyId = roomData?.hostiles?.[0] || null;
- const enemyData = rawEnemyId ? GameDataManager.getEnemy(rawEnemyId) : 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); // Скидаємо HP для нового ворога
+ setEnemyHp(null);
addLog(`--- ENTERING SECTOR ${data.roomIndex + 1} ---`);
});
- // Слухаємо результати бою від сервера
socket.on("dungeon:combat_result", (data) => {
if (data.message) addLog(data.message);
- if (data.enemyHp !== undefined) setEnemyHp(data.enemyHp);
+ 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.");
@@ -49,7 +62,7 @@ const DungeonScreen = ({ session, socket }) => {
socket.off("dungeon:room_update");
socket.off("dungeon:combat_result");
};
- }, [socket]);
+ }, [socket, currentEnemy]);
const addLog = (text) => {
const time = new Date().toLocaleTimeString([], {
@@ -62,15 +75,12 @@ const DungeonScreen = ({ session, socket }) => {
};
const handleCombat = () => {
- if (isEnemyDefeated) return;
- socket.emit("dungeon:combat_step", { enemyId: rawEnemyId });
- addLog(
- `Initiating strike sequence on ${enemyData?.displayName || "Target"}...`,
- );
+ if (isEnemyDefeated || !currentEnemy) return;
+ socket.emit("dungeon:combat_step", { enemyId: currentEnemy.id });
+ addLog(`Initiating strike sequence...`);
};
const handleLoot = () => {
- socket.emit("dungeon:get_loot");
setIsLooted(true);
addLog("Loot encryption bypassed. Resources transferred.");
};
@@ -103,11 +113,34 @@ const DungeonScreen = ({ session, socket }) => {
+
+
ENVIRONMENT_SCAN
+
+
+
+
+
+
{getRoomDisplayName(roomData)}
+
+ TYPE: {hostiles.length > 0 ? "COMBAT_ZONE" : "SECURE_AREA"}
+
+
+
+
+
- {enemyData ? (
+ {currentEnemy ? (
{isEnemyDefeated ? "SIGNAL_LOST" : "HOSTILE_DETECTED"}
@@ -117,7 +150,9 @@ const DungeonScreen = ({ session, socket }) => {
className={`fas ${isEnemyDefeated ? "fa-skull-crossbones" : "fa-robot"}`}
>
-
{enemyData.displayName}
+
+ {getEnemyDisplayName(currentEnemy)}
+
STRUCTURE INTEGRITY
@@ -132,8 +167,8 @@ const DungeonScreen = ({ session, socket }) => {
- LVL: {enemyData.level || 1}
- ID: {GameDataManager._cleanId(rawEnemyId)}
+ LVL: {currentEnemy.level || 1}
+ {currentEnemy.id}
) : (
@@ -158,7 +193,7 @@ const DungeonScreen = ({ session, socket }) => {
- {!isEnemyDefeated && enemyData && (
+ {!isEnemyDefeated && currentEnemy && (
@@ -170,7 +205,7 @@ const DungeonScreen = ({ session, socket }) => {
)}
- {(isLooted || !enemyData) && (
+ {(isLooted || !currentEnemy) && (
diff --git a/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_ambush_zone.json b/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_ambush_zone.json
index b0c5332..e020d8a 100644
--- a/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_ambush_zone.json
+++ b/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_ambush_zone.json
@@ -1,6 +1,6 @@
{
"rooms": {
- "id": "original:pirate_ambush_zone",
+ "id": "original:pirate/pirate_ambush_zone",
"hostiles": [
"original:pirates/scout_drone",
"original:pirates/raider_frigate",
diff --git a/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_boss_bridge.json b/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_boss_bridge.json
index 6a1d7ee..2c9e294 100644
--- a/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_boss_bridge.json
+++ b/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_boss_bridge.json
@@ -1,6 +1,6 @@
{
"rooms": {
- "id": "original:pirate_boss_bridge",
+ "id": "original:pirate/pirate_boss_bridge",
"hostiles": ["original:pirates/black_mark_cruiser"],
"gainXp": 100,
"credits": 2500
diff --git a/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_patrol_room.json b/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_patrol_room.json
index fe26cb9..cda0c2e 100644
--- a/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_patrol_room.json
+++ b/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_patrol_room.json
@@ -1,6 +1,6 @@
{
"rooms": {
- "id": "original:pirate_patrol_room",
+ "id": "original:pirate/pirate_patrol_room",
"hostiles": [
"original:pirates/scout_drone",
"original:pirates/scout_drone"
diff --git a/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_supply_bay.json b/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_supply_bay.json
index 54be8fe..6171c9e 100644
--- a/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_supply_bay.json
+++ b/game-server/datapacks/original/data/enemies/rooms/pirates/pirate_supply_bay.json
@@ -1,6 +1,6 @@
{
"rooms": {
- "id": "original:pirate_supply_bay",
+ "id": "original:pirate/pirate_supply_bay",
"hostiles": [],
"gainXp": 5,
"credits": 800,
diff --git a/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_boss.json b/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_boss.json
index edb6201..f60ef65 100644
--- a/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_boss.json
+++ b/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_boss.json
@@ -1,6 +1,6 @@
{
"rooms": {
- "id": "original:tutorial/tutorial_boss",
+ "id": "original:tutorial/tutorial_boss_room",
"hostiles": ["original:tutorial/tutorial_boss_hostile"],
"gainXp": 4,
"credits": 200
diff --git a/game-server/src/game/DungeonManager.js b/game-server/src/game/DungeonManager.js
new file mode 100644
index 0000000..b4d741c
--- /dev/null
+++ b/game-server/src/game/DungeonManager.js
@@ -0,0 +1,86 @@
+const DatapackLoader = require("./DatapackLoader");
+
+class DungeonManager {
+ constructor() {
+ this.activeSessions = new Map();
+ }
+
+ startDungeon(playerId, dungeonId) {
+ const dungeon = DatapackLoader.getDungeon(dungeonId);
+ if (!dungeon || !dungeon.rooms?.length) return null;
+
+ const session = {
+ dungeonId,
+ currentRoomIndex: 0,
+ isFinished: false,
+ currentEnemyHp: undefined,
+ rewards: { xp: 0, credits: 0, items: [] },
+ };
+
+ this.activeSessions.set(playerId, session);
+ return this.getCurrentRoomData(playerId);
+ }
+
+ getCurrentRoomData(playerId) {
+ const session = this.activeSessions.get(playerId);
+ if (!session) return null;
+
+ const dungeon = DatapackLoader.getDungeon(session.dungeonId);
+ const roomRef = dungeon.rooms[session.currentRoomIndex];
+ const roomData = DatapackLoader.getRoom(roomRef.id);
+ if (!roomData) return null;
+
+ const hostiles = (roomData.hostiles || [])
+ .map((hId) => {
+ const hostile = DatapackLoader.getEnemy(hId);
+ return hostile ? { ...hostile } : null;
+ })
+ .filter(Boolean);
+
+ return {
+ roomIndex: session.currentRoomIndex,
+ totalRooms: dungeon.rooms.length,
+ config: roomData,
+ hostiles,
+ };
+ }
+
+ moveToNextRoom(playerId) {
+ const session = this.activeSessions.get(playerId);
+ if (!session || session.isFinished) return null;
+
+ const dungeon = DatapackLoader.getDungeon(session.dungeonId);
+ const roomRef = dungeon.rooms[session.currentRoomIndex];
+ const currentRoom = DatapackLoader.getRoom(roomRef.id);
+
+ if (currentRoom) {
+ session.rewards.xp += currentRoom.gainXp || 0;
+ session.rewards.credits += currentRoom.credits || 0;
+
+ if (currentRoom.loot && Array.isArray(currentRoom.loot)) {
+ currentRoom.loot.forEach((item) => {
+ session.rewards.items.push({ ...item });
+ });
+ }
+ }
+
+ session.currentEnemyHp = undefined;
+
+ if (session.currentRoomIndex < dungeon.rooms.length - 1) {
+ session.currentRoomIndex++;
+ return this.getCurrentRoomData(playerId);
+ }
+
+ session.isFinished = true;
+ return {
+ status: "completed",
+ rewards: session.rewards,
+ };
+ }
+
+ leaveDungeon(playerId) {
+ this.activeSessions.delete(playerId);
+ }
+}
+
+module.exports = new DungeonManager();
diff --git a/game-server/src/sockets/handlers/dungeonHandler.js b/game-server/src/sockets/handlers/dungeonHandler.js
index b4f3d58..a3e0934 100644
--- a/game-server/src/sockets/handlers/dungeonHandler.js
+++ b/game-server/src/sockets/handlers/dungeonHandler.js
@@ -1,6 +1,6 @@
const { Player, Inventory } = require("../../models");
-const sessionManager = require("../../game/SessionManager");
const DatapackLoader = require("../../game/DatapackLoader");
+const dungeonManager = require("../../game/DungeonManager");
module.exports = (io, socket) => {
const userId = socket.user?.id;
@@ -8,80 +8,71 @@ module.exports = (io, socket) => {
socket.on("dungeon:start", async ({ dungeonId }) => {
try {
if (!userId) return;
-
const dungeon = DatapackLoader.getDungeon(dungeonId);
if (!dungeon) {
- return socket.emit("error", { message: "Dungeon coordinates invalid" });
+ return socket.emit("error", { message: "Dungeon not found" });
}
const player = await Player.findByPk(userId);
- if (player.energy < dungeon.energyCost) {
+ const energyCost = dungeon.meta?.energyCost || 0;
+
+ if (player.energy < energyCost) {
+ return socket.emit("error", { message: "Insufficient energy" });
+ }
+
+ await player.decrement("energy", { by: energyCost });
+
+ const firstRoom = dungeonManager.startDungeon(userId, dungeonId);
+ if (!firstRoom) {
return socket.emit("error", {
- message: "Insufficient energy for deployment",
+ message: "Failed to initialize dungeon",
});
}
- await player.decrement("energy", { by: dungeon.energyCost });
-
- const dungeonSession = {
- dungeonId: dungeon.id,
- currentRoomIndex: 0,
- isCompleted: false,
- startTime: Date.now(),
- rewards: [],
- };
-
- sessionManager.setPlayerScene(socket.id, "dungeon", dungeonSession);
-
- const firstRoomId = dungeon.rooms[0].id;
- const firstRoomData = DatapackLoader.getRoom(firstRoomId);
-
socket.emit("dungeon:started", {
dungeonId: dungeon.id,
- room: firstRoomData,
- roomIndex: 0,
- totalRooms: dungeon.rooms.length,
- remainingEnergy: player.energy - dungeon.energyCost,
+ room: firstRoom.config,
+ hostiles: firstRoom.hostiles,
+ roomIndex: firstRoom.roomIndex,
+ totalRooms: firstRoom.totalRooms,
+ remainingEnergy: player.energy - energyCost,
});
-
- console.log(`[Dungeon] Player ${userId} started ${dungeonId}`);
} catch (err) {
console.error("Dungeon Start Error:", err);
- socket.emit("error", { message: "Failed to initiate deployment" });
+ socket.emit("error", { message: "Critical deployment failure" });
}
});
- socket.on("dungeon:combat_step", async (data) => {
+ socket.on("dungeon:combat_step", async ({ enemyId }) => {
try {
- const session = sessionManager.getPlayerSession(socket.id);
- if (!session || session.scene !== "dungeon" || !session.sceneData) return;
+ if (!userId) return;
+
+ const session = dungeonManager.activeSessions.get(userId);
+ if (!session || session.isFinished) return;
- const dungeonSession = session.sceneData;
- const enemyId = sessionManager._cleanId(data.enemyId);
const enemyTemplate = DatapackLoader.getEnemy(enemyId);
-
if (!enemyTemplate) {
return socket.emit("error", { message: "Target data corrupted" });
}
- if (dungeonSession.currentEnemyHp === undefined) {
- dungeonSession.currentEnemyHp = enemyTemplate.stats?.hp || 100;
+ if (session.currentEnemyHp === undefined) {
+ session.currentEnemyHp = enemyTemplate.stats?.health || 100;
}
- const damage = Math.floor(Math.random() * 11) + 15;
- dungeonSession.currentEnemyHp -= damage;
+ const damage = Math.floor(Math.random() * 10) + 20;
+ session.currentEnemyHp -= damage;
- const isDefeated = dungeonSession.currentEnemyHp <= 0;
+ const isDefeated = session.currentEnemyHp <= 0;
socket.emit("dungeon:combat_result", {
damageDealt: damage,
- enemyHp: Math.max(0, dungeonSession.currentEnemyHp),
+ enemyHp: Math.max(0, session.currentEnemyHp),
targetDefeated: isDefeated,
- message: `Strike successful. Dealt ${damage} damage to units.`,
+ message: `Strike successful. Dealt ${damage} damage.`,
});
if (isDefeated) {
- console.log(`[Dungeon] Enemy ${enemyId} defeated by ${session.id}`);
+ session.currentEnemyHp = undefined;
}
} catch (err) {
console.error("Combat Error:", err);
@@ -90,62 +81,49 @@ module.exports = (io, socket) => {
socket.on("dungeon:next_room", async () => {
try {
- const session = sessionManager.getPlayerSession(socket.id);
+ if (!userId) return;
- if (!session || session.scene !== "dungeon" || !session.sceneData) {
- console.error(
- `[Dungeon Error] Invalid session for socket ${socket.id}`,
- );
- return;
+ const nextRoom = dungeonManager.moveToNextRoom(userId);
+ if (!nextRoom) {
+ return socket.emit("error", {
+ message: "Could not proceed to next room",
+ });
}
- const dungeonId = session.sceneData.dungeonId; // ТУТ була помилка (читання з undefined)
- const dungeon = DatapackLoader.getDungeon(dungeonId);
-
- const nextIndex = session.sceneData.currentRoomIndex + 1;
-
- if (nextIndex < dungeon.rooms.length) {
- session.sceneData.currentRoomIndex = nextIndex;
-
- const nextRoomId = dungeon.rooms[nextIndex].id;
- const nextRoomData = DatapackLoader.getRoom(nextRoomId);
-
- socket.emit("dungeon:room_update", {
- room: nextRoomData,
- roomIndex: nextIndex,
- });
+ if (nextRoom.status === "completed") {
+ await finalizeDungeon(socket, nextRoom.rewards);
} else {
- await finalizeDungeon(socket, session.sceneData, dungeon);
+ socket.emit("dungeon:room_update", {
+ room: nextRoom.config,
+ hostiles: nextRoom.hostiles,
+ roomIndex: nextRoom.roomIndex,
+ });
}
} catch (err) {
console.error("Dungeon Progress Error:", err);
- socket.emit("error", { message: "Failed to process next room" });
+ socket.emit("error", { message: "Navigation system error" });
}
});
+
+ socket.on("dungeon:leave", () => {
+ if (userId) dungeonManager.leaveDungeon(userId);
+ });
};
-async function finalizeDungeon(socket, sessionData, dungeon) {
+async function finalizeDungeon(socket, sessionRewards) {
const userId = socket.user.id;
- const earnedLoot = [];
+ try {
+ const player = await Player.findByPk(userId);
- dungeon.lootTable.forEach((entry) => {
- if (Math.random() * 100 <= entry.chance) {
- earnedLoot.push({ itemId: entry.itemId, quantity: 1 });
+ if (sessionRewards.credits > 0) {
+ await player.increment("credits", { by: sessionRewards.credits });
}
- });
- for (const item of earnedLoot) {
- const [invItem, created] = await Inventory.findOrCreate({
- where: { playerId: userId, itemId: item.itemId },
- defaults: { quantity: 0 },
+ socket.emit("dungeon:completed", {
+ rewards: sessionRewards,
+ message: "Mission successful. All objectives secured.",
});
- await invItem.increment("quantity", { by: item.quantity });
+ } finally {
+ dungeonManager.leaveDungeon(userId);
}
-
- sessionManager.setPlayerScene(socket.id, "world", null);
-
- socket.emit("dungeon:completed", {
- rewards: earnedLoot,
- message: "Mission successful. All objectives secured.",
- });
}