From 86bfae222866d86793bd4a9f9c24299bb0c5830a Mon Sep 17 00:00:00 2001 From: MaksSlyzar Date: Sat, 18 Apr 2026 14:47:20 +0300 Subject: [PATCH] Update Dungeonsystem --- client/src/services/GameDataManager.js | 1 - .../tabs/styles/InventoryTab.css | 38 +++++++++++++++ .../data/dungeons/tutorial/tutorial.json | 10 ++-- .../tutorial/tutorial_boss_hostile.json | 15 +++++- .../enemies/rooms/tutorial/tutorial_boss.json | 8 +++- .../rooms/tutorial/tutorial_enemy_room.json | 8 +++- .../rooms/tutorial/tutorial_loot_room.json | 16 ++++++- game-server/src/game/DatapackLoader.js | 2 +- game-server/src/game/DungeonManager.js | 40 +++++++++++----- .../src/sockets/handlers/dungeonHandler.js | 48 +++++++++++++++++-- 10 files changed, 159 insertions(+), 27 deletions(-) diff --git a/client/src/services/GameDataManager.js b/client/src/services/GameDataManager.js index f38d400..a23f486 100644 --- a/client/src/services/GameDataManager.js +++ b/client/src/services/GameDataManager.js @@ -31,7 +31,6 @@ class GameDataManager { if (Array.isArray(data.rooms)) { data.rooms.forEach((r) => this.rooms.set(r.id, r)); } - console.log(this.hostiles); if (data.languages) { this.translations = data.languages; } diff --git a/client/src/views/GameInterface/tabs/styles/InventoryTab.css b/client/src/views/GameInterface/tabs/styles/InventoryTab.css index 9243ec2..eb07ac6 100644 --- a/client/src/views/GameInterface/tabs/styles/InventoryTab.css +++ b/client/src/views/GameInterface/tabs/styles/InventoryTab.css @@ -233,3 +233,41 @@ border-radius: 3px; z-index: 2; } + +.qty-label { + position: absolute; + bottom: 2px; + right: 4px; + font-size: 11px; + font-weight: 800; + color: #fff; + text-shadow: + 1px 1px 0 #000, + -1px -1px 0 #000, + 1px -1px 0 #000, + -1px 1px 0 #000, + 0 0 5px rgba(0, 0, 0, 0.8); + pointer-events: none; + z-index: 3; +} + +.item-slot { + width: 60px; + height: 60px; + background: rgba(5, 8, 12, 0.9); + border: 1px solid #1a2638; + display: flex; + align-items: center; + justify-content: center; + position: relative; + cursor: pointer; + transition: 0.2s; + overflow: visible; +} + +.item-img-grid { + max-width: 80%; + max-height: 80%; + object-fit: contain; + pointer-events: none; +} diff --git a/game-server/datapacks/original/data/dungeons/tutorial/tutorial.json b/game-server/datapacks/original/data/dungeons/tutorial/tutorial.json index 66ae565..4e81896 100644 --- a/game-server/datapacks/original/data/dungeons/tutorial/tutorial.json +++ b/game-server/datapacks/original/data/dungeons/tutorial/tutorial.json @@ -3,12 +3,14 @@ "id": "original:tutorial/tutorial_dungeon", "displayName": "dungeons.original.tutorial.tutorial", "description": "dungeons.original.tutorial.tutorial.desc", - "meta":{ + "meta": { "energyCost": 0, "repeatable": false, - "missionArea":"space", - "raid": false, "_comment_1":"Future raid type picking, when you can have friends in the dugeon to help you.", - "missionAllowed": [], "_comment_2":"Future ship type picking, when ship classes are started" + "missionArea": "space", + "raid": false, + "_comment_1": "Future raid type picking, when you can have friends in the dugeon to help you.", + "missionAllowed": [], + "_comment_2": "Future ship type picking, when ship classes are started" }, "rooms": [ { "id": "original:tutorial/tutorial_enemy_room" }, diff --git a/game-server/datapacks/original/data/enemies/hostiles/tutorial/tutorial_boss_hostile.json b/game-server/datapacks/original/data/enemies/hostiles/tutorial/tutorial_boss_hostile.json index 3863b07..908825c 100644 --- a/game-server/datapacks/original/data/enemies/hostiles/tutorial/tutorial_boss_hostile.json +++ b/game-server/datapacks/original/data/enemies/hostiles/tutorial/tutorial_boss_hostile.json @@ -8,6 +8,19 @@ "damage": 4, "critical.chance": 0.3, "attack.rate": 2 - } + }, + "loot": [ + { + "id": "original:ore_coal", + "chance": 1.0, + "count": 50 + }, + { + "id": "original:ore_copper", + "chance": 1.0, + "count": 20 + } + ], + "meta": {} } } 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 f60ef65..dcb1b2d 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,8 +1,14 @@ { "rooms": { "id": "original:tutorial/tutorial_boss_room", + "displayName": "rooms.original.tutorial.tutorial_boss_room.name", + "description": "rooms.original.tutorial.tutorial_boss_room.desc", "hostiles": ["original:tutorial/tutorial_boss_hostile"], "gainXp": 4, - "credits": 200 + "credits": 200, + "loot": [], + "meta": { + "isBossRoom": true + } } } diff --git a/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_enemy_room.json b/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_enemy_room.json index 04c3a93..1234a19 100644 --- a/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_enemy_room.json +++ b/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_enemy_room.json @@ -1,8 +1,14 @@ { "rooms": { "id": "original:tutorial/tutorial_enemy_room", + "displayName": "rooms.original.tutorial.tutorial_enemy_room.name", + "description": "rooms.original.tutorial.tutorial_enemy_room.desc", "hostiles": ["original:tutorial/tutorial_hostile"], "gainXp": 3, - "credits": 30 + "credits": 30, + "loot": [], + "meta": { + "isBossRoom": false + } } } diff --git a/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_loot_room.json b/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_loot_room.json index 38580dd..6a7812b 100644 --- a/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_loot_room.json +++ b/game-server/datapacks/original/data/enemies/rooms/tutorial/tutorial_loot_room.json @@ -1,6 +1,20 @@ { "rooms": { "id": "original:tutorial/tutorial_loot_room", - "loot": [{ "id": "original:bio_pulp", "count": 1 }] + "displayName": "rooms.original.tutorial.tutorial_loot_room.name", + "description": "rooms.original.tutorial.tutorial_loot_room.desc", + "hostiles": [], + "gainXp": 0, + "credits": 0, + "loot": [ + { + "id": "original:bio_pulp", + "chance": 1.0, + "count": 1 + } + ], + "meta": { + "isBossRoom": false + } } } diff --git a/game-server/src/game/DatapackLoader.js b/game-server/src/game/DatapackLoader.js index 0253602..55a868f 100644 --- a/game-server/src/game/DatapackLoader.js +++ b/game-server/src/game/DatapackLoader.js @@ -147,7 +147,7 @@ class DatapackLoader { const data = json[typeKey]; if (!data) return; - const fullId = `${packName}:${data.id}`; + const fullId = `${data.id}`; switch (typeKey) { case "armour": diff --git a/game-server/src/game/DungeonManager.js b/game-server/src/game/DungeonManager.js index b4d741c..a6c525a 100644 --- a/game-server/src/game/DungeonManager.js +++ b/game-server/src/game/DungeonManager.js @@ -16,7 +16,6 @@ class DungeonManager { currentEnemyHp: undefined, rewards: { xp: 0, credits: 0, items: [] }, }; - this.activeSessions.set(playerId, session); return this.getCurrentRoomData(playerId); } @@ -27,16 +26,17 @@ class DungeonManager { const dungeon = DatapackLoader.getDungeon(session.dungeonId); const roomRef = dungeon.rooms[session.currentRoomIndex]; - const roomData = DatapackLoader.getRoom(roomRef.id); - if (!roomData) return null; + const rawRoom = DatapackLoader.getRoom(roomRef.id); + + if (!rawRoom) return null; + + const roomData = rawRoom; const hostiles = (roomData.hostiles || []) .map((hId) => { - const hostile = DatapackLoader.getEnemy(hId); - return hostile ? { ...hostile } : null; + return DatapackLoader.getEnemy(hId); }) .filter(Boolean); - return { roomIndex: session.currentRoomIndex, totalRooms: dungeon.rooms.length, @@ -51,15 +51,29 @@ class DungeonManager { const dungeon = DatapackLoader.getDungeon(session.dungeonId); const roomRef = dungeon.rooms[session.currentRoomIndex]; - const currentRoom = DatapackLoader.getRoom(roomRef.id); + const rawRoom = DatapackLoader.getRoom(roomRef.id); - if (currentRoom) { - session.rewards.xp += currentRoom.gainXp || 0; - session.rewards.credits += currentRoom.credits || 0; + if (rawRoom && rawRoom.room) { + const room = rawRoom.room; - if (currentRoom.loot && Array.isArray(currentRoom.loot)) { - currentRoom.loot.forEach((item) => { - session.rewards.items.push({ ...item }); + session.rewards.xp += room.gainXp || 0; + session.rewards.credits += room.credits || 0; + + if (room.loot && Array.isArray(room.loot)) { + room.loot.forEach((lootEntry) => { + const roll = Math.random(); + if (roll <= (lootEntry.chance || 1.0)) { + session.rewards.items.push({ + id: lootEntry.id, + count: + typeof lootEntry.count === "object" + ? Math.floor( + Math.random() * + (lootEntry.count.max - lootEntry.count.min + 1), + ) + lootEntry.count.min + : lootEntry.count || 1, + }); + } }); } } diff --git a/game-server/src/sockets/handlers/dungeonHandler.js b/game-server/src/sockets/handlers/dungeonHandler.js index a3e0934..945532f 100644 --- a/game-server/src/sockets/handlers/dungeonHandler.js +++ b/game-server/src/sockets/handlers/dungeonHandler.js @@ -50,25 +50,48 @@ module.exports = (io, socket) => { const session = dungeonManager.activeSessions.get(userId); if (!session || session.isFinished) return; - const enemyTemplate = DatapackLoader.getEnemy(enemyId); - if (!enemyTemplate) { + const rawEnemy = DatapackLoader.getEnemy(enemyId); + if (!rawEnemy || !rawEnemy) { return socket.emit("error", { message: "Target data corrupted" }); } + const enemy = rawEnemy; + if (session.currentEnemyHp === undefined) { - session.currentEnemyHp = enemyTemplate.stats?.health || 100; + session.currentEnemyHp = enemy.stats?.health || 100; } const damage = Math.floor(Math.random() * 10) + 20; session.currentEnemyHp -= damage; const isDefeated = session.currentEnemyHp <= 0; + let lootDropped = []; + + if (isDefeated && enemy.loot) { + enemy.loot.forEach((entry) => { + if (Math.random() <= (entry.chance || 1.0)) { + const count = + typeof entry.count === "object" + ? Math.floor( + Math.random() * (entry.count.max - entry.count.min + 1), + ) + entry.count.min + : entry.count || 1; + + const drop = { id: entry.id, count }; + lootDropped.push(drop); + session.rewards.items.push(drop); + } + }); + } socket.emit("dungeon:combat_result", { damageDealt: damage, enemyHp: Math.max(0, session.currentEnemyHp), targetDefeated: isDefeated, - message: `Strike successful. Dealt ${damage} damage.`, + loot: lootDropped, + message: isDefeated + ? `Enemy eliminated!` + : `Strike successful. Dealt ${damage} damage.`, }); if (isDefeated) { @@ -119,10 +142,27 @@ async function finalizeDungeon(socket, sessionRewards) { await player.increment("credits", { by: sessionRewards.credits }); } + if (sessionRewards.xp > 0 && player.xp !== undefined) { + await player.increment("xp", { by: sessionRewards.xp }); + } + + if (sessionRewards.items.length > 0) { + for (const item of sessionRewards.items) { + const [invItem, created] = await Inventory.findOrCreate({ + where: { playerId: userId, itemId: item.id }, + defaults: { quantity: 0 }, + }); + + await invItem.increment("quantity", { by: item.count }); + } + } + socket.emit("dungeon:completed", { rewards: sessionRewards, message: "Mission successful. All objectives secured.", }); + } catch (err) { + console.error("Finalize Error:", err); } finally { dungeonManager.leaveDungeon(userId); }