Fixed inventory equipment, crafting tab scroll
Some checks are pending
Deploy MMO Server / deploy (push) Waiting to run
Some checks are pending
Deploy MMO Server / deploy (push) Waiting to run
This commit is contained in:
parent
2a266becea
commit
75897440c9
@ -36,7 +36,7 @@ class GameDataManager {
|
||||
data.quests.forEach((q) => this.quests.set(q.id, q));
|
||||
}
|
||||
|
||||
console.log(this.skills);
|
||||
console.log(this.recipes);
|
||||
if (data.languages) {
|
||||
this.translations = data.languages;
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ const Navigation = ({ activeTab, onTabChange }) => {
|
||||
if (id === "itemlist") return "ITEM_LIST";
|
||||
if (id === "chat") return "CHAT";
|
||||
if (id === "notifications") return "ALERTS";
|
||||
return GameDataManager.t(`category.tabs.original.${id}`);
|
||||
return GameDataManager.t(`category.tabs.core.${id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -2,14 +2,12 @@ import React, { useState, useEffect } from "react";
|
||||
import { useSocket } from "../../../hooks/useSocket";
|
||||
import GameDataManager from "../../../services/GameDataManager";
|
||||
import "./styles/CraftingTab.css";
|
||||
import CategorySelector from "../components/CategorySelector";
|
||||
import CraftModal from "./components/CraftModal";
|
||||
import { config } from "../../../config/api";
|
||||
import MeteorRegion from "../../../components/Meteor/MeteorRegion.jsx";
|
||||
|
||||
const CraftingTab = () => {
|
||||
const { socket } = useSocket();
|
||||
|
||||
const [categories, setCategories] = useState([]);
|
||||
const [recipes, setRecipes] = useState([]);
|
||||
const [userInventory, setUserInventory] = useState([]);
|
||||
@ -27,9 +25,7 @@ const CraftingTab = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (activeCategory) {
|
||||
const filteredRecipes =
|
||||
GameDataManager.getRecipesByCategory(activeCategory);
|
||||
setRecipes(filteredRecipes);
|
||||
setRecipes(GameDataManager.getRecipesByCategory(activeCategory));
|
||||
}
|
||||
}, [activeCategory]);
|
||||
|
||||
@ -43,8 +39,7 @@ const CraftingTab = () => {
|
||||
|
||||
const handleCraftStarted = (data) => {
|
||||
const recipeData = GameDataManager.getRecipe(data.recipeId);
|
||||
const now = Date.now();
|
||||
const diff = (data.finishAt - now) / 1000;
|
||||
const diff = (data.finishAt - Date.now()) / 1000;
|
||||
|
||||
if (diff <= 0) {
|
||||
setActiveCraft(null);
|
||||
@ -59,9 +54,7 @@ const CraftingTab = () => {
|
||||
timeLeft: Math.max(0, Math.ceil(diff)),
|
||||
});
|
||||
|
||||
if (recipeData) {
|
||||
setSelectedRecipe(recipeData);
|
||||
}
|
||||
if (recipeData) setSelectedRecipe(recipeData);
|
||||
};
|
||||
|
||||
const handleCraftSuccess = () => {
|
||||
@ -83,11 +76,11 @@ const CraftingTab = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeCraft) return;
|
||||
|
||||
const timer = setInterval(() => {
|
||||
const now = Date.now();
|
||||
const diff = Math.max(0, Math.ceil((activeCraft.finishAt - now) / 1000));
|
||||
|
||||
const diff = Math.max(
|
||||
0,
|
||||
Math.ceil((activeCraft.finishAt - Date.now()) / 1000),
|
||||
);
|
||||
if (diff <= 0) {
|
||||
clearInterval(timer);
|
||||
setActiveCraft(null);
|
||||
@ -95,7 +88,6 @@ const CraftingTab = () => {
|
||||
setActiveCraft((prev) => (prev ? { ...prev, timeLeft: diff } : null));
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, [activeCraft?.finishAt]);
|
||||
|
||||
@ -119,8 +111,7 @@ const CraftingTab = () => {
|
||||
<div className="active-craft-panel">
|
||||
<div className="craft-info">
|
||||
<span>
|
||||
<i className="fas fa-hammer fa-spin"></i> Assembling:{" "}
|
||||
{activeCraft.name}
|
||||
<i className="fas fa-hammer fa-spin"></i> {activeCraft.name}
|
||||
</span>
|
||||
<span className="time-left">{activeCraft.timeLeft}s</span>
|
||||
</div>
|
||||
@ -137,27 +128,32 @@ const CraftingTab = () => {
|
||||
|
||||
<div className="crafting-header">
|
||||
<h2>
|
||||
<i className="fas fa-microchip"></i> Fabrication Unit
|
||||
<i className="fas fa-microchip"></i> Fabrication
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<CategorySelector
|
||||
categories={categories}
|
||||
activeCategory={activeCategory}
|
||||
onCategoryChange={setActiveCategory}
|
||||
/>
|
||||
<div className="crafting-categories">
|
||||
{categories.map((cat) => (
|
||||
<button
|
||||
key={cat.id}
|
||||
className={`crafting-cat-btn ${activeCategory === cat.id ? "active" : ""}`}
|
||||
onClick={() => setActiveCategory(cat.id)}
|
||||
>
|
||||
{cat.displayName || cat.id}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="crafting-grid">
|
||||
{recipes.map((recipe) => {
|
||||
const isThisRecipeCrafting = activeCraft?.recipeId === recipe.id;
|
||||
const isCrafting = activeCraft?.recipeId === recipe.id;
|
||||
const canCraft = recipe.ingredients.every(
|
||||
(ing) => getOwnedAmount(ing.itemId) >= ing.quantity,
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={recipe.id}
|
||||
className={`recipe-card ${!canCraft && !isThisRecipeCrafting ? "insufficient-resources" : ""} ${isThisRecipeCrafting ? "crafting-active" : ""}`}
|
||||
className={`recipe-card ${!canCraft && !isCrafting ? "insufficient-resources" : ""} ${isCrafting ? "crafting-active" : ""}`}
|
||||
onClick={() => setSelectedRecipe(recipe)}
|
||||
>
|
||||
<div className="recipe-icon">
|
||||
@ -165,21 +161,17 @@ const CraftingTab = () => {
|
||||
<img
|
||||
width={64}
|
||||
src={`${config.serverUrl}/static/${recipe.texture}`}
|
||||
alt={recipe.displayName}
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
<div className="fallback-icon">{recipe.displayName[0]}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="recipe-info-main">
|
||||
<span className="recipe-name">{recipe.displayName}</span>
|
||||
<div className="recipe-badges">
|
||||
<span className="badge-time">
|
||||
<i className="fas fa-clock"></i> {recipe.time_seconds}s
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{isThisRecipeCrafting && (
|
||||
<span className="recipe-name">{recipe.displayName}</span>
|
||||
<span className="badge-time">
|
||||
<i className="fas fa-clock"></i> {recipe.time_seconds}s
|
||||
</span>
|
||||
{isCrafting && (
|
||||
<div className="craft-overlay-mini">
|
||||
<i className="fas fa-sync fa-spin"></i>
|
||||
</div>
|
||||
|
||||
@ -19,7 +19,6 @@ const InventoryTab = () => {
|
||||
|
||||
const manifest = GameDataManager.manifest || {};
|
||||
const coreSystems = manifest.core_systems?.categories || {};
|
||||
|
||||
const getFullTextureUrl = (path) => {
|
||||
if (!path) return "/assets/no-image.png";
|
||||
if (path.startsWith("http")) return path;
|
||||
@ -89,36 +88,43 @@ const InventoryTab = () => {
|
||||
|
||||
const equipmentSlots = {
|
||||
personal: Object.keys(coreSystems)
|
||||
.filter((k) => {
|
||||
const system = coreSystems[k];
|
||||
return (
|
||||
system.slotType === "personal" &&
|
||||
!k.includes("accessory") &&
|
||||
k !== "original:weapon"
|
||||
);
|
||||
})
|
||||
.map((k) => ({
|
||||
id: k,
|
||||
label: GameDataManager.t(coreSystems[k].displayName),
|
||||
})),
|
||||
|
||||
weapons: Object.keys(coreSystems)
|
||||
.filter((k) => k === "original:weapon")
|
||||
.map((k) => ({
|
||||
id: k,
|
||||
label: GameDataManager.t(coreSystems[k].displayName),
|
||||
})),
|
||||
|
||||
accessories: Object.keys(coreSystems)
|
||||
.filter(
|
||||
(k) =>
|
||||
k.startsWith("original:personal_") &&
|
||||
!k.includes("accessory") &&
|
||||
k !== "original:personal_weapons",
|
||||
k.includes("accessory") && coreSystems[k].slotType === "personal",
|
||||
)
|
||||
.map((k) => ({
|
||||
id: k,
|
||||
label: GameDataManager.t(coreSystems[k].displayName),
|
||||
})),
|
||||
weapons: Object.keys(coreSystems)
|
||||
.filter((k) => k === "original:personal_weapons")
|
||||
.map((k) => ({
|
||||
id: k,
|
||||
label: GameDataManager.t(coreSystems[k].displayName),
|
||||
})),
|
||||
accessories: Object.keys(coreSystems)
|
||||
.filter((k) => k.includes("personal_accessory"))
|
||||
.map((k) => ({
|
||||
id: k,
|
||||
label: GameDataManager.t(coreSystems[k].displayName),
|
||||
})),
|
||||
|
||||
ship: Object.keys(coreSystems)
|
||||
.filter((k) => k.startsWith("original:ship_"))
|
||||
.filter((k) => coreSystems[k].slotType === "ship")
|
||||
.map((k) => ({
|
||||
id: k,
|
||||
label: GameDataManager.t(coreSystems[k].displayName),
|
||||
})),
|
||||
};
|
||||
|
||||
const renderSlotGroup = (title, groupSlots) => (
|
||||
<div className="equip-group">
|
||||
<div className="group-label">{title}</div>
|
||||
|
||||
@ -3,83 +3,56 @@
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.crafting-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.crafting-header h2 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.crafting-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.recipe-card {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.recipe-card:hover {
|
||||
transform: translateY(-5px);
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.recipe-icon img {
|
||||
max-width: 50px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.recipe-name {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.2;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge-time {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
margin: 0;
|
||||
color: var(--primary-color);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.crafting-categories {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
padding: 5px 0;
|
||||
margin-bottom: 20px;
|
||||
padding: 5px 5px 12px 5px;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--primary-color) transparent;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.crafting-categories::-webkit-scrollbar {
|
||||
display: none;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
.crafting-categories::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.crafting-categories::-webkit-scrollbar-thumb {
|
||||
background: var(--primary-color);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.crafting-cat-btn {
|
||||
flex: 0 0 auto;
|
||||
padding: 8px 16px;
|
||||
padding: 10px 18px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid var(--border-color);
|
||||
color: var(--text-secondary);
|
||||
font-family: "Orbitron", sans-serif;
|
||||
font-size: 0.75rem;
|
||||
@ -90,56 +63,105 @@
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.crafting-cat-btn.active {
|
||||
background: var(--primary-color);
|
||||
.crafting-cat-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-color: var(--primary-color);
|
||||
color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.crafting-cat-btn.active {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(0, 243, 255, 0.15) 0%,
|
||||
transparent 100%
|
||||
);
|
||||
border-color: var(--primary-color);
|
||||
border-left: 3px solid var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
font-weight: 700;
|
||||
box-shadow: inset 5px 0 10px rgba(0, 243, 255, 0.05);
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.crafting-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.crafting-header {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.crafting-header h2 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.crafting-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.recipe-card {
|
||||
padding: 10px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.recipe-icon img {
|
||||
max-width: 40px;
|
||||
}
|
||||
|
||||
.recipe-name {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.crafting-cat-btn {
|
||||
padding: 6px 12px;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
.crafting-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 15px;
|
||||
overflow-y: auto;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
height: 100%;
|
||||
.crafting-grid::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.crafting-grid::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.recipe-card {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.recipe-card:hover {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border-color: var(--primary-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.recipe-card.insufficient-resources {
|
||||
opacity: 0.6;
|
||||
filter: grayscale(0.5);
|
||||
}
|
||||
|
||||
.recipe-card.crafting-active {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 15px rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.active-craft-panel {
|
||||
background: rgba(0, 243, 255, 0.05);
|
||||
border: 1px solid var(--primary-color);
|
||||
padding: 12px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.craft-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-family: "Orbitron", sans-serif;
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.progress-bar-bg {
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.meteor-region-content {
|
||||
width: 100%;
|
||||
.progress-bar-fill {
|
||||
height: 100%;
|
||||
background: var(--primary-color);
|
||||
box-shadow: 0 0 10px var(--primary-color);
|
||||
transition: width 1s linear;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.crafting-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user