-
e.stopPropagation()}>
-
+
e.stopPropagation()}>
+
-
{item.name}
+
+ {item.displayName || item.name}
+
{item.rarity}
{item.description}
-
- Quality
- {item.rarity.toUpperCase()}
-
- {item.stats &&
}
- {item.stats && Object.entries(item.stats).map(([statName, value]) => (
-
- {formatStatName(statName)}
- +{value}
-
- ))}
+ {item.stats &&
+ Object.entries(item.stats).map(([statName, value]) => (
+
+
+ {" "}
+ {formatStatName
+ ? formatStatName(statName)
+ : statName.toUpperCase()}
+
+ +{value}
+
+ ))}
-
+ {isEquipped ? (
+
+ ) : (
+ item.canEquip && (
+
+ )
+ )}
diff --git a/client/src/views/MainMenu/sections/ServerSection.jsx b/client/src/views/MainMenu/sections/ServerSection.jsx
index 7a77478..50183f5 100644
--- a/client/src/views/MainMenu/sections/ServerSection.jsx
+++ b/client/src/views/MainMenu/sections/ServerSection.jsx
@@ -7,18 +7,17 @@ const ServerSection = ({ onBack, onSelect }) => {
const [servers, setServers] = useState([]);
const [loading, setLoading] = useState(false);
const [joiningId, setJoiningId] = useState(null);
- const [searchTerm, setSearchTerm] = useState(""); // Стан для пошуку
+ const [searchTerm, setSearchTerm] = useState("");
const fetchServers = async () => {
setLoading(true);
const startTime = Date.now();
try {
- const response = await axios.get(
- "http://localhost:3000/api/servers/list",
- );
+ const API_URL = import.meta.env.API_URL;
+ const response = await axios.get(`${API_URL}/api/servers/list`);
const elapsedTime = Date.now() - startTime;
- const remainingTime = Math.max(0, 2000 - elapsedTime); // Трохи зменшив штучну затримку
+ const remainingTime = Math.max(0, 2000 - elapsedTime);
await new Promise((resolve) => setTimeout(resolve, remainingTime));
setServers(response.data);
@@ -59,7 +58,6 @@ const ServerSection = ({ onBack, onSelect }) => {
fetchServers();
}, []);
- // Фільтрація серверів на основі searchTerm
const filteredServers = servers.filter((server) =>
searchTerm.length > 0
? server.name?.toLowerCase().includes(searchTerm.toLowerCase())
diff --git a/game-server/src/game/InventoryManager.js b/game-server/src/game/InventoryManager.js
new file mode 100644
index 0000000..100e5c4
--- /dev/null
+++ b/game-server/src/game/InventoryManager.js
@@ -0,0 +1,59 @@
+const { Player, Inventory } = require("../models");
+const DatapackLoader = require("./DatapackLoader");
+
+class InventoryManager {
+ async getInventory(playerId) {
+ return await Inventory.findAll({
+ where: { playerId },
+ attributes: ["itemId", "quantity"],
+ });
+ }
+
+ async getEquipment(playerId) {
+ const player = await Player.findByPk(playerId);
+ if (!player) return {};
+
+ const slots = [
+ "personal_helmet",
+ "personal_suit",
+ "personal_gloves",
+ "personal_backpack",
+ "personal_boots",
+ "personal_weapons",
+ "ship_hull",
+ "ship_shields",
+ "ship_engines",
+ ];
+
+ const equipment = {};
+ slots.forEach((slot) => {
+ equipment[slot] = player[`equipped_${slot}`] || null;
+ });
+ return equipment;
+ }
+
+ async equipItem(playerId, itemId, slot) {
+ const hasItem = await Inventory.findOne({ where: { playerId, itemId } });
+ if (!hasItem) throw new Error("ITEM_NOT_FOUND");
+
+ const itemInfo = DatapackLoader.getItem(itemId);
+ if (!itemInfo) throw new Error("INVALID_ITEM_DATA");
+
+ const allowedSlot = itemInfo.meta?.equipmentSlot;
+ if (allowedSlot !== slot) {
+ throw new Error("INVALID_SLOT_FOR_ITEM");
+ }
+
+ const dbField = `equipped_${slot}`.replace("original:", "");
+ await Player.update({ [dbField]: itemId }, { where: { id: playerId } });
+ return itemInfo;
+ }
+
+ async unequipItem(playerId, slot) {
+ const dbField = `equipped_${slot}`;
+ await Player.update({ [dbField]: null }, { where: { id: playerId } });
+ return true;
+ }
+}
+
+module.exports = new InventoryManager();
diff --git a/game-server/src/game/SessionManager.js b/game-server/src/game/SessionManager.js
index cd938ee..74e9188 100644
--- a/game-server/src/game/SessionManager.js
+++ b/game-server/src/game/SessionManager.js
@@ -16,19 +16,31 @@ class SessionManager {
sceneData: null,
joinedAt: Date.now(),
equipment: {
- weapon: playerRaw.equippedWeapon,
- armor: playerRaw.equippedArmor,
- engine: playerRaw.equippedEngine,
- shield: playerRaw.equippedShield,
- helmet: playerRaw.equippedHelmet,
- boots: playerRaw.equippedBoots,
- hands: playerRaw.equippedHands,
- pants: playerRaw.equippedPants,
- accessory: playerRaw.equippedAccessory,
+ personal_helmet: playerRaw.equipped_personal_helmet,
+ personal_suit: playerRaw.equipped_personal_suit,
+ personal_gloves: playerRaw.equipped_personal_gloves,
+ personal_backpack: playerRaw.equipped_personal_backpack,
+ personal_boots: playerRaw.equipped_personal_boots,
+ personal_weapons: playerRaw.equipped_personal_weapons,
+
+ ship_hull: playerRaw.equipped_ship_hull,
+ ship_shields: playerRaw.equipped_ship_shields,
+ ship_engines: playerRaw.equipped_ship_engines,
+ ship_weapon_1: playerRaw.equipped_ship_weapon_1,
+ ship_weapon_2: playerRaw.equipped_ship_weapon_2,
},
});
}
+ updateEquipment(socketId, slot, itemId) {
+ const session = this.sessions.get(socketId);
+ if (session && session.equipment.hasOwnProperty(slot)) {
+ session.equipment[slot] = itemId;
+ } else if (session) {
+ session.equipment[slot] = itemId;
+ }
+ }
+
setPlayerScene(socketId, sceneName, data = null) {
const session = this.sessions.get(socketId);
if (session) {
@@ -43,13 +55,6 @@ class SessionManager {
return this.sessions.get(socketId);
}
- updateEquipment(socketId, slot, itemId) {
- const session = this.sessions.get(socketId);
- if (session) {
- session.equipment[slot] = itemId;
- }
- }
-
updateStatus(socketId, newStatus, location = null) {
const session = this.sessions.get(socketId);
if (session) {
diff --git a/game-server/src/models/Player.js b/game-server/src/models/Player.js
index 5c708ec..9679098 100644
--- a/game-server/src/models/Player.js
+++ b/game-server/src/models/Player.js
@@ -42,14 +42,22 @@ const Player = sequelize.define("Player", {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
- equippedWeapon: { type: DataTypes.STRING },
- equippedArmor: { type: DataTypes.STRING },
- equippedEngine: { type: DataTypes.STRING },
- equippedAccessory: { type: DataTypes.STRING },
- equippedHelmet: { type: DataTypes.STRING },
- equippedBoots: { type: DataTypes.STRING },
- equippedHands: { type: DataTypes.STRING },
- equippedPants: { type: DataTypes.STRING },
+
+ equipped_personal_helmet: { type: DataTypes.STRING },
+ equipped_personal_suit: { type: DataTypes.STRING },
+ equipped_personal_gloves: { type: DataTypes.STRING },
+ equipped_personal_backpack: { type: DataTypes.STRING },
+ equipped_personal_boots: { type: DataTypes.STRING },
+ equipped_personal_weapons: { type: DataTypes.STRING },
+
+ equipped_personal_accessory_1: { type: DataTypes.STRING },
+ equipped_personal_accessory_2: { type: DataTypes.STRING },
+
+ equipped_ship_hull: { type: DataTypes.STRING },
+ equipped_ship_shields: { type: DataTypes.STRING },
+ equipped_ship_engines: { type: DataTypes.STRING },
+ equipped_ship_weapon_1: { type: DataTypes.STRING },
+ equipped_ship_weapon_2: { type: DataTypes.STRING },
});
module.exports = Player;
diff --git a/game-server/src/sockets/handlers/inventoryHandler.js b/game-server/src/sockets/handlers/inventoryHandler.js
index e5a31ae..074ecea 100644
--- a/game-server/src/sockets/handlers/inventoryHandler.js
+++ b/game-server/src/sockets/handlers/inventoryHandler.js
@@ -1,82 +1,35 @@
-const { Player, Inventory } = require("../../models");
+const inventoryManager = require("../../game/InventoryManager");
const sessionManager = require("../../game/SessionManager");
module.exports = (io, socket) => {
const userId = socket.user?.id;
-
if (!userId) return;
socket.on("player:get_inventory", async () => {
try {
- const userItems = await Inventory.findAll({
- where: { playerId: userId },
- attributes: ["itemId", "quantity"],
- });
-
- socket.emit("player:inventory_data", userItems);
+ const items = await inventoryManager.getInventory(userId);
+ socket.emit("player:inventory_data", items);
} catch (err) {
- console.error("Inventory Fetch Error:", err.message);
- socket.emit("error", { message: "Failed to load inventory" });
+ socket.emit("error", { message: "LOAD_INVENTORY_FAILED" });
}
});
socket.on("player:get_equipment", async () => {
try {
- const player = await Player.findByPk(userId);
- if (!player) return;
-
- const slots = [
- "Weapon",
- "Armor",
- "Helmet",
- "Boots",
- "Hands",
- "Pants",
- "Engine",
- "Accessory",
- ];
- const equipment = {};
-
- slots.forEach((slot) => {
- const itemId = player[`equipped${slot}`];
- equipment[slot.toLowerCase()] = itemId || null;
- });
-
+ const equipment = await inventoryManager.getEquipment(userId);
socket.emit("player:equipment_data", equipment);
} catch (err) {
- console.error("Equipment Fetch Error:", err.message);
+ console.error(err);
}
});
socket.on("player:equip_item", async ({ itemId, slot }) => {
try {
- const hasItem = await Inventory.findOne({
- where: { playerId: userId, itemId: itemId },
- });
-
- if (!hasItem) {
- return socket.emit("error", { message: "You don't own this item" });
- }
-
- const DatapackLoader = require("../../game/DatapackLoader");
- const itemInfo = DatapackLoader.getItem(itemId);
- if (!itemInfo) return;
-
- if (slot === "weapon" && itemInfo.type !== "weapon")
- return socket.emit("error", { message: "Not a weapon" });
- if (
- ["helmet", "armor", "boots", "pants", "hands"].includes(slot) &&
- itemInfo.type !== "armour"
- ) {
- return socket.emit("error", { message: "Not armor" });
- }
-
- const slotField = `equipped${slot.charAt(0).toUpperCase() + slot.slice(1)}`;
- await Player.update({ [slotField]: itemId }, { where: { id: userId } });
+ const itemInfo = await inventoryManager.equipItem(userId, itemId, slot);
sessionManager.updateEquipment(socket.id, slot, itemId);
- socket.emit("player:item_equipped", { slot, itemId: itemId });
+ socket.emit("player:item_equipped", { slot, itemId });
socket.broadcast.emit("player:visible_changed", {
playerId: userId,
@@ -84,21 +37,23 @@ module.exports = (io, socket) => {
texturePath: itemInfo.texture,
});
} catch (err) {
- console.error("Equip Error:", err.message);
- socket.emit("error", { message: "Equip failed" });
+ socket.emit("error", { message: err.message });
}
});
socket.on("player:unequip_item", async ({ slot }) => {
try {
- const slotField = `equipped${slot.charAt(0).toUpperCase() + slot.slice(1)}`;
- await Player.update({ [slotField]: null }, { where: { id: userId } });
-
+ await inventoryManager.unequipItem(userId, slot);
sessionManager.updateEquipment(socket.id, slot, null);
socket.emit("player:item_unequipped", { slot });
+ socket.broadcast.emit("player:visible_changed", {
+ playerId: userId,
+ slot,
+ texturePath: null,
+ });
} catch (err) {
- console.error("Unequip Error:", err.message);
+ console.error(err);
}
});
};