Update Dungeonsystem

This commit is contained in:
MaksSlyzar 2026-04-18 14:47:20 +03:00
parent 54b85f0f1f
commit 86bfae2228
10 changed files with 159 additions and 27 deletions

View File

@ -31,7 +31,6 @@ class GameDataManager {
if (Array.isArray(data.rooms)) { if (Array.isArray(data.rooms)) {
data.rooms.forEach((r) => this.rooms.set(r.id, r)); data.rooms.forEach((r) => this.rooms.set(r.id, r));
} }
console.log(this.hostiles);
if (data.languages) { if (data.languages) {
this.translations = data.languages; this.translations = data.languages;
} }

View File

@ -233,3 +233,41 @@
border-radius: 3px; border-radius: 3px;
z-index: 2; 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;
}

View File

@ -7,8 +7,10 @@
"energyCost": 0, "energyCost": 0,
"repeatable": false, "repeatable": false,
"missionArea": "space", "missionArea": "space",
"raid": false, "_comment_1":"Future raid type picking, when you can have friends in the dugeon to help you.", "raid": false,
"missionAllowed": [], "_comment_2":"Future ship type picking, when ship classes are started" "_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": [ "rooms": [
{ "id": "original:tutorial/tutorial_enemy_room" }, { "id": "original:tutorial/tutorial_enemy_room" },

View File

@ -8,6 +8,19 @@
"damage": 4, "damage": 4,
"critical.chance": 0.3, "critical.chance": 0.3,
"attack.rate": 2 "attack.rate": 2
} },
"loot": [
{
"id": "original:ore_coal",
"chance": 1.0,
"count": 50
},
{
"id": "original:ore_copper",
"chance": 1.0,
"count": 20
}
],
"meta": {}
} }
} }

View File

@ -1,8 +1,14 @@
{ {
"rooms": { "rooms": {
"id": "original:tutorial/tutorial_boss_room", "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"], "hostiles": ["original:tutorial/tutorial_boss_hostile"],
"gainXp": 4, "gainXp": 4,
"credits": 200 "credits": 200,
"loot": [],
"meta": {
"isBossRoom": true
}
} }
} }

View File

@ -1,8 +1,14 @@
{ {
"rooms": { "rooms": {
"id": "original:tutorial/tutorial_enemy_room", "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"], "hostiles": ["original:tutorial/tutorial_hostile"],
"gainXp": 3, "gainXp": 3,
"credits": 30 "credits": 30,
"loot": [],
"meta": {
"isBossRoom": false
}
} }
} }

View File

@ -1,6 +1,20 @@
{ {
"rooms": { "rooms": {
"id": "original:tutorial/tutorial_loot_room", "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
}
} }
} }

View File

@ -147,7 +147,7 @@ class DatapackLoader {
const data = json[typeKey]; const data = json[typeKey];
if (!data) return; if (!data) return;
const fullId = `${packName}:${data.id}`; const fullId = `${data.id}`;
switch (typeKey) { switch (typeKey) {
case "armour": case "armour":

View File

@ -16,7 +16,6 @@ class DungeonManager {
currentEnemyHp: undefined, currentEnemyHp: undefined,
rewards: { xp: 0, credits: 0, items: [] }, rewards: { xp: 0, credits: 0, items: [] },
}; };
this.activeSessions.set(playerId, session); this.activeSessions.set(playerId, session);
return this.getCurrentRoomData(playerId); return this.getCurrentRoomData(playerId);
} }
@ -27,16 +26,17 @@ class DungeonManager {
const dungeon = DatapackLoader.getDungeon(session.dungeonId); const dungeon = DatapackLoader.getDungeon(session.dungeonId);
const roomRef = dungeon.rooms[session.currentRoomIndex]; 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 || []) const hostiles = (roomData.hostiles || [])
.map((hId) => { .map((hId) => {
const hostile = DatapackLoader.getEnemy(hId); return DatapackLoader.getEnemy(hId);
return hostile ? { ...hostile } : null;
}) })
.filter(Boolean); .filter(Boolean);
return { return {
roomIndex: session.currentRoomIndex, roomIndex: session.currentRoomIndex,
totalRooms: dungeon.rooms.length, totalRooms: dungeon.rooms.length,
@ -51,15 +51,29 @@ class DungeonManager {
const dungeon = DatapackLoader.getDungeon(session.dungeonId); const dungeon = DatapackLoader.getDungeon(session.dungeonId);
const roomRef = dungeon.rooms[session.currentRoomIndex]; const roomRef = dungeon.rooms[session.currentRoomIndex];
const currentRoom = DatapackLoader.getRoom(roomRef.id); const rawRoom = DatapackLoader.getRoom(roomRef.id);
if (currentRoom) { if (rawRoom && rawRoom.room) {
session.rewards.xp += currentRoom.gainXp || 0; const room = rawRoom.room;
session.rewards.credits += currentRoom.credits || 0;
if (currentRoom.loot && Array.isArray(currentRoom.loot)) { session.rewards.xp += room.gainXp || 0;
currentRoom.loot.forEach((item) => { session.rewards.credits += room.credits || 0;
session.rewards.items.push({ ...item });
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,
});
}
}); });
} }
} }

View File

@ -50,25 +50,48 @@ module.exports = (io, socket) => {
const session = dungeonManager.activeSessions.get(userId); const session = dungeonManager.activeSessions.get(userId);
if (!session || session.isFinished) return; if (!session || session.isFinished) return;
const enemyTemplate = DatapackLoader.getEnemy(enemyId); const rawEnemy = DatapackLoader.getEnemy(enemyId);
if (!enemyTemplate) { if (!rawEnemy || !rawEnemy) {
return socket.emit("error", { message: "Target data corrupted" }); return socket.emit("error", { message: "Target data corrupted" });
} }
const enemy = rawEnemy;
if (session.currentEnemyHp === undefined) { if (session.currentEnemyHp === undefined) {
session.currentEnemyHp = enemyTemplate.stats?.health || 100; session.currentEnemyHp = enemy.stats?.health || 100;
} }
const damage = Math.floor(Math.random() * 10) + 20; const damage = Math.floor(Math.random() * 10) + 20;
session.currentEnemyHp -= damage; session.currentEnemyHp -= damage;
const isDefeated = session.currentEnemyHp <= 0; 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", { socket.emit("dungeon:combat_result", {
damageDealt: damage, damageDealt: damage,
enemyHp: Math.max(0, session.currentEnemyHp), enemyHp: Math.max(0, session.currentEnemyHp),
targetDefeated: isDefeated, targetDefeated: isDefeated,
message: `Strike successful. Dealt ${damage} damage.`, loot: lootDropped,
message: isDefeated
? `Enemy eliminated!`
: `Strike successful. Dealt ${damage} damage.`,
}); });
if (isDefeated) { if (isDefeated) {
@ -119,10 +142,27 @@ async function finalizeDungeon(socket, sessionRewards) {
await player.increment("credits", { by: sessionRewards.credits }); 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", { socket.emit("dungeon:completed", {
rewards: sessionRewards, rewards: sessionRewards,
message: "Mission successful. All objectives secured.", message: "Mission successful. All objectives secured.",
}); });
} catch (err) {
console.error("Finalize Error:", err);
} finally { } finally {
dungeonManager.leaveDungeon(userId); dungeonManager.leaveDungeon(userId);
} }