diff --git a/Client-Server/electron-main copy.js b/Client-Server/electron-main copy.js
deleted file mode 100644
index 3296162..0000000
--- a/Client-Server/electron-main copy.js
+++ /dev/null
@@ -1,451 +0,0 @@
-const { app, BrowserWindow, Menu, shell, ipcMain } = require('electron');
-const path = require('path');
-const fs = require('fs');
-const logger = require('./js/core/Logger');
-
-console.log('[MAIN PROCESS] Electron main process starting...');
-console.log('[MAIN PROCESS] Node.js version:', process.version);
-console.log('[MAIN PROCESS] Electron version:', process.versions.electron);
-console.log('[MAIN PROCESS] Platform:', process.platform);
-console.log('[MAIN PROCESS] Current working directory:', process.cwd());
-
-// Keep a global reference of the window object
-let mainWindow;
-
-function createWindow() {
- console.log('[MAIN PROCESS] createWindow() called');
-
- try {
- console.log('[MAIN PROCESS] Creating BrowserWindow...');
- // Create the browser window
- mainWindow = new BrowserWindow({
- width: 1200,
- height: 832, // 800 + 32px for custom title bar
- minWidth: 1200,
- minHeight: 832,
- maxWidth: 1200,
- maxHeight: 832,
- resizable: false,
- frame: false,
- titleBarStyle: 'hidden',
- webPreferences: {
- nodeIntegration: true,
- contextIsolation: false,
- enableRemoteModule: true,
- webSecurity: true
- },
- icon: path.join(__dirname, 'assets/icon.png'),
- show: false, // Don't show until ready-to-show
- title: 'Galaxy Strike Online'
- });
-
- console.log('[MAIN PROCESS] BrowserWindow created successfully');
- console.log('[MAIN PROCESS] Loading index.html...');
-
- // Load the index.html file
- mainWindow.loadFile('index.html');
-
- console.log('[MAIN PROCESS] index.html loaded, setting up electronAPI...');
-
- // Set up electronAPI after DOM is ready
- mainWindow.webContents.on('dom-ready', () => {
- console.log('[MAIN PROCESS] DOM is ready, setting up electronAPI...');
- mainWindow.webContents.executeJavaScript(`
- console.log('[RENDERER] Setting up electronAPI...');
- window.electronAPI = {
- minimizeWindow: () => require('electron').ipcRenderer.send('minimize-window'),
- closeWindow: () => require('electron').ipcRenderer.send('close-window'),
- toggleFullscreen: () => require('electron').ipcRenderer.send('toggle-fullscreen'),
- log: (level, message, data) => require('electron').ipcRenderer.send('log-message', { level, message, data }),
- createSaveFolders: (saveSlots) => require('electron').ipcRenderer.invoke('create-save-folders', saveSlots),
- testFileAccess: (slotPath) => require('electron').ipcRenderer.invoke('test-file-access', slotPath),
- saveGame: (slot, saveData) => require('electron').ipcRenderer.invoke('save-game', slot, saveData),
- loadGame: (slot) => require('electron').ipcRenderer.invoke('load-game', slot),
- getPath: (name) => require('electron').ipcRenderer.invoke('get-path', name),
- deleteSaveFile: (slot) => require('electron').ipcRenderer.invoke('delete-save-file', slot)
- };
- console.log('[RENDERER] electronAPI setup completed');
- `).then(() => {
- console.log('[MAIN PROCESS] electronAPI setup completed');
- }).catch((error) => {
- console.error('[MAIN PROCESS] Failed to setup electronAPI:', error);
- });
- });
-
- // Show window when ready
- mainWindow.once('ready-to-show', () => {
- console.log('[MAIN PROCESS] Window ready-to-show event fired');
- mainWindow.show();
- });
-
- // Open DevTools in development
- if (process.argv.includes('--dev')) {
- console.log('[MAIN PROCESS] Opening DevTools...');
- mainWindow.webContents.openDevTools();
- }
-
- // Handle window closed
- mainWindow.on('closed', () => {
- console.log('[MAIN PROCESS] Window closed event fired');
- mainWindow = null;
- });
-
- // Handle renderer process crashes
- mainWindow.webContents.on('render-process-gone', (event, details) => {
- console.error('[MAIN PROCESS] Renderer process crashed:', details);
- console.error('[MAIN PROCESS] Crash reason:', details.reason);
- console.error('[MAIN PROCESS] Exit code:', details.exitCode);
- });
-
- // Handle renderer process unresponsive
- mainWindow.webContents.on('unresponsive', () => {
- console.warn('[MAIN PROCESS] Renderer process unresponsive');
- });
-
- // Handle renderer process responsive again
- mainWindow.webContents.on('responsive', () => {
- console.log('[MAIN PROCESS] Renderer process responsive again');
- });
-
- // Handle console messages from renderer
- mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => {
- console.log(`[RENDERER CONSOLE] [${level}] ${message} (line: ${line}, source: ${sourceId})`);
- });
-
- // Handle page load errors
- mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL, isMainFrame) => {
- console.error('[MAIN PROCESS] Page failed to load:', errorCode, errorDescription, validatedURL);
- });
-
- // Handle page load success
- mainWindow.webContents.on('did-finish-load', () => {
- console.log('[MAIN PROCESS] Page finished loading');
- });
-
- // Handle DOM ready
- mainWindow.webContents.on('dom-ready', () => {
- console.log('[MAIN PROCESS] DOM is ready');
- });
-
- // Handle external links
- mainWindow.webContents.setWindowOpenHandler(({ url }) => {
- console.log('[MAIN PROCESS] External link requested:', url);
- shell.openExternal(url);
- return { action: 'deny' };
- });
-
- console.log('[MAIN PROCESS] createWindow() completed successfully');
-
- } catch (error) {
- console.error('[MAIN PROCESS] Error in createWindow():', error);
- console.error('[MAIN PROCESS] Error stack:', error.stack);
- }
-}
-
-// IPC handlers for save operations
-ipcMain.handle('create-save-folders', async (event, saveSlots) => {
- console.log('[MAIN PROCESS] create-save-folders called with saveSlots:', saveSlots);
- try {
- const userDataPath = app.getPath('userData');
- console.log('[MAIN PROCESS] userDataPath:', userDataPath);
- const savesDir = path.join(userDataPath, 'saves');
- console.log('[MAIN PROCESS] savesDir:', savesDir);
-
- // Create main saves directory
- if (!fs.existsSync(savesDir)) {
- console.log('[MAIN PROCESS] Creating saves directory:', savesDir);
- fs.mkdirSync(savesDir, { recursive: true });
- console.log('[MAIN PROCESS] Saves directory created successfully');
- } else {
- console.log('[MAIN PROCESS] Saves directory already exists');
- }
-
- const paths = {
- base: savesDir,
- slots: []
- };
-
- // Create save slot directories
- for (let i = 1; i <= saveSlots; i++) {
- const slotDir = path.join(savesDir, `slot${i}`);
- console.log(`[MAIN PROCESS] Checking/creating slot ${i} directory:`, slotDir);
- if (!fs.existsSync(slotDir)) {
- console.log(`[MAIN PROCESS] Creating slot ${i} directory`);
- fs.mkdirSync(slotDir, { recursive: true });
-
- // Create initial save info file
- const saveInfo = {
- slot: i,
- created: new Date().toISOString(),
- version: '1.0.0',
- exists: false
- };
-
- const infoPath = path.join(slotDir, 'saveinfo.json');
- fs.writeFileSync(infoPath, JSON.stringify(saveInfo, null, 2));
- console.log(`[MAIN PROCESS] Created save info for slot ${i}`);
- } else {
- console.log(`[MAIN PROCESS] Slot ${i} directory already exists`);
- }
- paths.slots.push(slotDir);
- }
-
- console.log('[MAIN PROCESS] Save folders created successfully, returning paths:', paths);
- return { success: true, paths };
- } catch (error) {
- console.error('[MAIN PROCESS] Failed to create save folders:', error);
- return { success: false, error: error.message };
- }
-});
-
-ipcMain.handle('test-file-access', async (event, slotPath) => {
- try {
- const testFile = path.join(slotPath, 'access_test.txt');
- fs.writeFileSync(testFile, 'test');
- fs.unlinkSync(testFile);
- return { success: true };
- } catch (error) {
- return { success: false, error: error.message };
- }
-});
-
-ipcMain.handle('save-game', async (event, slot, saveData) => {
- try {
- const userDataPath = app.getPath('userData');
- const savesDir = path.join(userDataPath, 'saves');
- const slotDir = path.join(savesDir, `slot${slot}`);
-
- // Save game data
- const saveFilePath = path.join(slotDir, 'save.json');
- fs.writeFileSync(saveFilePath, JSON.stringify(saveData, null, 2));
-
- // Update save info
- const infoPath = path.join(slotDir, 'saveinfo.json');
- const saveInfo = {
- slot: slot,
- created: new Date().toISOString(),
- lastSaved: new Date().toISOString(),
- version: '1.0.0',
- exists: true,
- playTime: saveData.gameTime || 0
- };
- fs.writeFileSync(infoPath, JSON.stringify(saveInfo, null, 2));
-
- return { success: true };
- } catch (error) {
- console.error('Failed to save game:', error);
- return { success: false, error: error.message };
- }
-});
-
-ipcMain.handle('load-game', async (event, slot) => {
- try {
- const userDataPath = app.getPath('userData');
- const savesDir = path.join(userDataPath, 'saves');
- const slotDir = path.join(savesDir, `slot${slot}`);
- const saveFilePath = path.join(slotDir, 'save.json');
-
- if (fs.existsSync(saveFilePath)) {
- const saveContent = fs.readFileSync(saveFilePath, 'utf8');
- const saveData = JSON.parse(saveContent);
- return { success: true, data: saveData };
- } else {
- return { success: false, error: 'Save file not found' };
- }
- } catch (error) {
- console.error('Failed to load game:', error);
- return { success: false, error: error.message };
- }
-});
-
-ipcMain.handle('get-path', async (event, name) => {
- try {
- return app.getPath(name);
- } catch (error) {
- return null;
- }
-});
-
-ipcMain.handle('delete-save-file', async (event, slot) => {
- console.log('[MAIN PROCESS] delete-save-file called for slot:', slot);
- try {
- const userDataPath = app.getPath('userData');
- const savesDir = path.join(userDataPath, 'saves');
- const slotDir = path.join(savesDir, `slot${slot}`);
- const saveFilePath = path.join(slotDir, 'save.json');
- const infoFilePath = path.join(slotDir, 'saveinfo.json');
-
- console.log('[MAIN PROCESS] Attempting to delete save files from:', slotDir);
-
- let deletedFiles = [];
-
- // Delete save file if it exists
- if (fs.existsSync(saveFilePath)) {
- console.log('[MAIN PROCESS] Deleting save file:', saveFilePath);
- fs.unlinkSync(saveFilePath);
- deletedFiles.push('save.json');
- }
-
- // Delete save info file if it exists
- if (fs.existsSync(infoFilePath)) {
- console.log('[MAIN PROCESS] Deleting save info file:', infoFilePath);
- fs.unlinkSync(infoFilePath);
- deletedFiles.push('saveinfo.json');
- }
-
- // Create empty save info file to indicate slot is empty
- const saveInfo = {
- slot: slot,
- created: new Date().toISOString(),
- version: '1.0.0',
- exists: false,
- deleted: new Date().toISOString()
- };
- fs.writeFileSync(infoFilePath, JSON.stringify(saveInfo, null, 2));
-
- console.log('[MAIN PROCESS] Successfully deleted save files for slot', slot, ':', deletedFiles);
- return { success: true, deletedFiles };
- } catch (error) {
- console.error('[MAIN PROCESS] Failed to delete save file:', error);
- return { success: false, error: error.message };
- }
-});
-
-// IPC handlers for window controls
-// Handle logging from renderer process
-ipcMain.on('log-message', async (event, { level, message, data }) => {
- try {
- switch (level) {
- case 'error':
- await logger.error(message, data);
- break;
- case 'warn':
- await logger.warn(message, data);
- break;
- case 'info':
- await logger.info(message, data);
- break;
- case 'debug':
- await logger.debug(message, data);
- break;
- default:
- await logger.info(message, data);
- }
- } catch (error) {
- console.error('Failed to log message from renderer:', error);
- // Fallback to console logging to prevent infinite loops
- console.log(`[${level}] ${message}`, data || '');
- }
-});
-
-ipcMain.on('minimize-window', () => {
- if (mainWindow) {
- mainWindow.minimize();
- }
-});
-
-ipcMain.on('close-window', () => {
- if (mainWindow) {
- mainWindow.close();
- }
-});
-
-ipcMain.on('toggle-fullscreen', () => {
- if (mainWindow) {
- const isFullscreen = mainWindow.isFullScreen();
- if (isFullscreen) {
- mainWindow.setFullScreen(false);
- mainWindow.setSize(1200, 832);
- mainWindow.center();
- } else {
- mainWindow.setFullScreen(true);
- }
- }
-});
-
-// This method will be called when Electron has finished initialization
-app.whenReady().then(async () => {
- console.log('[MAIN PROCESS] Electron app ready, starting initialization...');
-
- try {
- // Initialize logger with app data path
- console.log('[MAIN PROCESS] Initializing logger...');
- await logger.initialize(app.getPath('userData'));
- console.log('[MAIN PROCESS] Logger initialized');
-
- await logger.info('Galaxy Strike Online application starting');
- console.log('[MAIN PROCESS] Logger info message sent');
-
- console.log('[MAIN PROCESS] Creating main window...');
- createWindow();
-
- app.on('activate', () => {
- console.log('[MAIN PROCESS] Activate event fired');
- // On macOS it's common to re-create a window in the app when the dock icon is clicked
- if (BrowserWindow.getAllWindows().length === 0) {
- console.log('[MAIN PROCESS] No windows exist, creating new window');
- createWindow();
- }
- });
-
- console.log('[MAIN PROCESS] App initialization completed successfully');
-
- } catch (error) {
- console.error('[MAIN PROCESS] Error during app initialization:', error);
- console.error('[MAIN PROCESS] Error stack:', error.stack);
- }
-}).catch((error) => {
- console.error('[MAIN PROCESS] Error in app.whenReady():', error);
- console.error('[MAIN PROCESS] Error stack:', error.stack);
-});
-
-// Quit when all windows are closed
-app.on('window-all-closed', () => {
- // On macOS it's common for applications and their menu bar to stay active
- if (process.platform !== 'darwin') {
- logger.info('Application shutting down');
- app.quit();
- }
-});
-
-// Handle uncaught exceptions
-process.on('uncaughtException', async (error) => {
- console.error('[MAIN PROCESS] Uncaught Exception:', error);
- console.error('[MAIN PROCESS] Uncaught Exception stack:', error.stack);
-
- try {
- if (logger && typeof logger.errorEvent === 'function') {
- await logger.errorEvent(error, 'Uncaught Exception in Main Process');
- }
- } catch (logError) {
- console.error('[MAIN PROCESS] Failed to log uncaught exception:', logError);
- }
-
- console.error('[MAIN PROCESS] Application will continue running despite uncaught exception');
-});
-
-// Handle unhandled promise rejections
-process.on('unhandledRejection', (reason, promise) => {
- console.error('[MAIN PROCESS] Unhandled Promise Rejection at:', promise, 'reason:', reason);
- console.error('[MAIN PROCESS] Rejection reason stack:', reason.stack);
-});
-
-// Handle unhandled rejections
-process.on('unhandledRejection', async (reason, promise) => {
- // Avoid logging the logging system's own errors to prevent infinite loops
- if (reason && reason.message && reason.message.includes('object could not be cloned')) {
- console.warn('IPC cloning error detected - this is expected during logger initialization');
- return;
- }
-
- await logger.error('Unhandled Rejection', { reason: reason.toString(), promise: promise.toString() });
- console.error('Unhandled Rejection at:', promise, 'reason:', reason);
-});
-
-// Security: Prevent new window creation
-app.on('web-contents-created', (event, contents) => {
- contents.on('new-window', (event, navigationUrl) => {
- event.preventDefault();
- shell.openExternal(navigationUrl);
- });
-});
diff --git a/Client-Server/index copy.html b/Client-Server/index copy.html
deleted file mode 100644
index 51f732f..0000000
--- a/Client-Server/index copy.html
+++ /dev/null
@@ -1,699 +0,0 @@
-
-
-
-
-
- Galaxy Strike Online - Space Idle MMORPG
-
-
-
-
-
-
-
-
-
-
-
-
-
- Galaxy Strike Online
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
GALAXY STRIKE ONLINE
-
-
Initializing Universe...
-
-
-
-
-
-
-
-
-
-
-
-
- Dashboard
-
-
-
- Dungeons
-
-
-
- Skills
-
-
-
- Base
-
-
-
- Quests
-
-
-
- Inventory
-
-
-
- Crafting
-
-
-
- Shop
-
-
-
-
-
-
-
-
-
-
Fleet Status
-
-
-
-
-
Flagship: Starter Cruiser
-
Health: 100%
-
-
-
-
-
-
-
Idle Progress
-
-
Offline Time: 0h 0m
-
Resources Gained: 0
-
Claim Rewards
-
-
-
- Total Kills
- 0
-
-
- Dungeons Cleared
- 0
-
-
- Play Time
- 0h 0m
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Select a dungeon to begin your adventure
-
-
-
-
-
-
-
-
-
-
- Combat
- Science
- Crafting
-
-
-
-
-
-
-
-
-
-
- Base Overview
- Base Visualization
- Ship Gallery
- Starbases
-
-
-
-
-
-
-
-
Base Information
-
-
- Power Usage:
- 0/100
-
-
- Storage:
- 1000
-
-
- Production Rate:
- 0/s
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Your Ships
-
-
-
-
-
Current Ship
-
-
-
-
-
-
-
Starter Cruiser
-
-
- Class:
- Light
-
-
- Level:
- 1
-
-
- Health:
- 100/100
-
-
- Attack:
- 10
-
-
- Defense:
- 5
-
-
- Speed:
- 15
-
-
-
-
-
-
-
-
-
-
Ships Collected
-
-
-
-
-
-
-
-
-
-
-
-
-
Starbase Management
-
-
-
-
-
-
Available Starbases
-
-
-
-
-
-
-
-
-
-
- Main Story
- Daily
- Weekly
- Completed
- Failed Quests
-
-
Daily quests reset in: 00:00:00
-
Weekly quests reset in: 0d 00:00
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Select an item to view details
-
-
-
-
-
-
-
-
-
-
- Weapons
- Armor
- Items
- Ships
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Initializing...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Client-Server/index.html b/Client-Server/index.html
deleted file mode 100644
index 51f732f..0000000
--- a/Client-Server/index.html
+++ /dev/null
@@ -1,699 +0,0 @@
-
-
-
-
-
- Galaxy Strike Online - Space Idle MMORPG
-
-
-
-
-
-
-
-
-
-
-
-
-
- Galaxy Strike Online
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
GALAXY STRIKE ONLINE
-
-
Initializing Universe...
-
-
-
-
-
-
-
-
-
-
-
-
- Dashboard
-
-
-
- Dungeons
-
-
-
- Skills
-
-
-
- Base
-
-
-
- Quests
-
-
-
- Inventory
-
-
-
- Crafting
-
-
-
- Shop
-
-
-
-
-
-
-
-
-
-
Fleet Status
-
-
-
-
-
Flagship: Starter Cruiser
-
Health: 100%
-
-
-
-
-
-
-
Idle Progress
-
-
Offline Time: 0h 0m
-
Resources Gained: 0
-
Claim Rewards
-
-
-
- Total Kills
- 0
-
-
- Dungeons Cleared
- 0
-
-
- Play Time
- 0h 0m
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Select a dungeon to begin your adventure
-
-
-
-
-
-
-
-
-
-
- Combat
- Science
- Crafting
-
-
-
-
-
-
-
-
-
-
- Base Overview
- Base Visualization
- Ship Gallery
- Starbases
-
-
-
-
-
-
-
-
Base Information
-
-
- Power Usage:
- 0/100
-
-
- Storage:
- 1000
-
-
- Production Rate:
- 0/s
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Your Ships
-
-
-
-
-
Current Ship
-
-
-
-
-
-
-
Starter Cruiser
-
-
- Class:
- Light
-
-
- Level:
- 1
-
-
- Health:
- 100/100
-
-
- Attack:
- 10
-
-
- Defense:
- 5
-
-
- Speed:
- 15
-
-
-
-
-
-
-
-
-
-
Ships Collected
-
-
-
-
-
-
-
-
-
-
-
-
-
Starbase Management
-
-
-
-
-
-
Available Starbases
-
-
-
-
-
-
-
-
-
-
- Main Story
- Daily
- Weekly
- Completed
- Failed Quests
-
-
Daily quests reset in: 00:00:00
-
Weekly quests reset in: 0d 00:00
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Select an item to view details
-
-
-
-
-
-
-
-
-
-
- Weapons
- Armor
- Items
- Ships
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Initializing...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Client-Server/js/LocalServer.js b/Client-Server/js/LocalServer.js
deleted file mode 100644
index de6d955..0000000
--- a/Client-Server/js/LocalServer.js
+++ /dev/null
@@ -1,418 +0,0 @@
-/**
- * Local Server for Singleplayer Mode
- * A simplified server that runs within the Electron client for offline/singleplayer functionality
- * NOTE: This version requires express, socket.io, and cors dependencies to be installed
- */
-
-class LocalServer {
- constructor() {
- this.app = null;
- this.server = null;
- this.io = null;
- this.port = null;
- this.isRunning = false;
- this.connectedClients = new Map();
-
- console.log('[LOCAL SERVER] LocalServer initialized');
- }
-
- async initialize() {
- try {
- // Try to require dependencies
- if (typeof require !== 'undefined') {
- const express = require('express');
- const { createServer } = require('http');
- const { Server } = require('socket.io');
- const cors = require('cors');
-
- this.setupExpress(express, cors);
- this.createServer = createServer;
- this.ServerClass = Server;
-
- console.log('[LOCAL SERVER] Dependencies loaded successfully');
- return true;
- } else {
- console.warn('[LOCAL SERVER] require() not available, running in browser context');
- return false;
- }
- } catch (error) {
- console.error('[LOCAL SERVER] Failed to load dependencies:', error.message);
- console.log('[LOCAL SERVER] Please install dependencies: npm install express socket.io cors');
- return false;
- }
- }
-
- setupExpress(express, cors) {
- // Initialize Express app
- this.app = express();
-
- // Middleware
- this.app.use(cors({
- origin: ["http://localhost:3000", "http://127.0.0.1:3000", "file://"],
- credentials: true
- }));
- this.app.use(express.json({ limit: '10mb' }));
- this.app.use(express.urlencoded({ extended: true }));
-
- // Health check endpoint
- this.app.get('/health', (req, res) => {
- res.status(200).json({
- status: 'OK',
- timestamp: new Date().toISOString(),
- uptime: process.uptime(),
- mode: 'local'
- });
- });
-
- // API version endpoint
- this.app.get('/api/ssc/version', (req, res) => {
- res.status(200).json({
- version: '1.0.0',
- service: 'galaxystrikeonline-local-server',
- timestamp: new Date().toISOString(),
- mode: 'local'
- });
- });
-
- // Mock authentication endpoints for singleplayer
- this.app.post('/api/auth/login', (req, res) => {
- const { email, password } = req.body;
-
- // Auto-authenticate for singleplayer mode
- const mockUser = {
- id: 'local-user',
- email: email || 'local@player.com',
- username: 'Local Player',
- token: 'local-token-' + Date.now(),
- createdAt: new Date().toISOString()
- };
-
- res.status(200).json({
- success: true,
- user: mockUser,
- token: mockUser.token,
- message: 'Logged in to local mode'
- });
- });
-
- this.app.post('/api/auth/register', (req, res) => {
- const { email, password, username } = req.body;
-
- // Auto-register for singleplayer mode
- const mockUser = {
- id: 'local-user',
- email: email || 'local@player.com',
- username: username || 'Local Player',
- token: 'local-token-' + Date.now(),
- createdAt: new Date().toISOString()
- };
-
- res.status(201).json({
- success: true,
- user: mockUser,
- token: mockUser.token,
- message: 'Registered in local mode'
- });
- });
-
- // Mock server browser endpoints
- this.app.get('/api/servers', (req, res) => {
- // Return a single local server
- const localServer = {
- id: 'local-server',
- name: 'Local Singleplayer',
- description: 'Your personal local server for singleplayer gaming',
- type: 'private',
- region: 'local',
- maxPlayers: 1,
- currentPlayers: 0,
- owner: 'Local Player',
- address: 'localhost',
- port: this.port,
- status: 'online',
- createdAt: new Date().toISOString(),
- ping: 0
- };
-
- res.status(200).json({
- success: true,
- servers: [localServer]
- });
- });
-
- // Mock game data endpoints
- this.app.get('/api/game/player/:id', (req, res) => {
- // Return player data from local storage if available
- const playerId = req.params.id;
- let saveData;
-
- try {
- // In Electron context, access localStorage differently
- if (typeof localStorage !== 'undefined') {
- saveData = localStorage.getItem(`gso_save_slot_1`);
- }
- } catch (error) {
- console.warn('[LOCAL SERVER] Could not access localStorage:', error);
- }
-
- if (saveData) {
- try {
- const parsedData = JSON.parse(saveData);
- res.status(200).json({
- success: true,
- player: parsedData.player
- });
- } catch (error) {
- res.status(500).json({
- success: false,
- error: 'Failed to parse save data'
- });
- }
- } else {
- res.status(404).json({
- success: false,
- error: 'No save data found'
- });
- }
- });
-
- this.app.post('/api/game/player/:id/save', (req, res) => {
- // Save player data to local storage
- const playerId = req.params.id;
- const playerData = req.body;
-
- try {
- let existingSaveData = '{}';
-
- // In Electron context, access localStorage differently
- if (typeof localStorage !== 'undefined') {
- existingSaveData = localStorage.getItem(`gso_save_slot_1`) || '{}';
- }
-
- const parsedExisting = JSON.parse(existingSaveData);
-
- // Merge player data
- parsedExisting.player = playerData;
- parsedExisting.lastSave = Date.now();
-
- if (typeof localStorage !== 'undefined') {
- localStorage.setItem(`gso_save_slot_1`, JSON.stringify(parsedExisting));
- }
-
- res.status(200).json({
- success: true,
- message: 'Player data saved locally'
- });
- } catch (error) {
- res.status(500).json({
- success: false,
- error: 'Failed to save player data'
- });
- }
- });
- }
-
- async start(port = 3004) {
- if (this.isRunning) {
- console.log('[LOCAL SERVER] Server is already running on port', this.port);
- return { success: false, error: 'Server already running' };
- }
-
- try {
- // Initialize dependencies if not already done
- if (!this.app) {
- const initialized = await this.initialize();
- if (!initialized) {
- return { success: false, error: 'Failed to initialize server dependencies' };
- }
- }
-
- this.port = port;
- this.server = this.createServer(this.app);
- this.io = new this.ServerClass(this.server, {
- cors: {
- origin: ["http://localhost:3000", "http://127.0.0.1:3000", "file://"],
- methods: ["GET", "POST"]
- }
- });
-
- // Setup Socket.IO handlers
- this.setupSocketHandlers();
-
- // Start the server
- await new Promise((resolve, reject) => {
- this.server.listen(port, (error) => {
- if (error) {
- reject(error);
- } else {
- resolve();
- }
- });
- });
-
- this.isRunning = true;
- console.log(`[LOCAL SERVER] Local server started on port ${port}`);
-
- return {
- success: true,
- port: port,
- url: `http://localhost:${port}`
- };
-
- } catch (error) {
- console.error('[LOCAL SERVER] Failed to start server:', error);
- return { success: false, error: error.message };
- }
- }
-
- setupSocketHandlers() {
- this.io.on('connection', (socket) => {
- console.log('[LOCAL SERVER] Client connected:', socket.id);
- this.connectedClients.set(socket.id, {
- connectedAt: Date.now(),
- playerData: null
- });
-
- // Handle authentication
- socket.on('authenticate', (data) => {
- console.log('[LOCAL SERVER] Authenticating client:', socket.id, data);
-
- // Auto-authenticate for local mode
- socket.emit('authenticated', {
- success: true,
- user: {
- id: 'local-user',
- username: 'Local Player',
- token: 'local-token-' + Date.now()
- }
- });
-
- // Update client info
- const clientInfo = this.connectedClients.get(socket.id);
- if (clientInfo) {
- clientInfo.playerData = {
- id: 'local-user',
- username: 'Local Player'
- };
- }
- });
-
- // Handle game data sync
- socket.on('saveGameData', (data) => {
- console.log('[LOCAL SERVER] Saving game data for:', socket.id);
-
- // Save to localStorage (this will be handled by the client-side save system)
- socket.emit('gameDataSaved', {
- success: true,
- timestamp: Date.now()
- });
- });
-
- socket.on('loadGameData', (data) => {
- console.log('[LOCAL SERVER] Loading game data for:', socket.id);
-
- // Load from localStorage (this will be handled by the client-side load system)
- let saveData;
-
- try {
- if (typeof localStorage !== 'undefined') {
- saveData = localStorage.getItem(`gso_save_slot_1`);
- }
- } catch (error) {
- console.warn('[LOCAL SERVER] Could not access localStorage:', error);
- }
-
- if (saveData) {
- try {
- const parsedData = JSON.parse(saveData);
- socket.emit('gameDataLoaded', {
- success: true,
- data: parsedData
- });
- } catch (error) {
- socket.emit('gameDataLoaded', {
- success: false,
- error: 'Failed to parse save data'
- });
- }
- } else {
- socket.emit('gameDataLoaded', {
- success: false,
- error: 'No save data found'
- });
- }
- });
-
- // Handle disconnection
- socket.on('disconnect', () => {
- console.log('[LOCAL SERVER] Client disconnected:', socket.id);
- this.connectedClients.delete(socket.id);
- });
-
- // Send welcome message
- socket.emit('welcome', {
- message: 'Connected to local server',
- serverInfo: {
- mode: 'local',
- port: this.port,
- timestamp: new Date().toISOString()
- }
- });
- });
- }
-
- async stop() {
- if (!this.isRunning) {
- console.log('[LOCAL SERVER] Server is not running');
- return { success: false, error: 'Server is not running' };
- }
-
- try {
- // Disconnect all clients
- if (this.io) {
- this.io.disconnectSockets();
- }
-
- // Close the server
- if (this.server) {
- await new Promise((resolve) => {
- this.server.close(resolve);
- });
- }
-
- this.isRunning = false;
- this.port = null;
- this.connectedClients.clear();
-
- console.log('[LOCAL SERVER] Local server stopped');
- return { success: true };
-
- } catch (error) {
- console.error('[LOCAL SERVER] Failed to stop server:', error);
- return { success: false, error: error.message };
- }
- }
-
- getStatus() {
- return {
- isRunning: this.isRunning,
- port: this.port,
- connectedClients: this.connectedClients.size,
- uptime: this.isRunning ? process.uptime() : 0
- };
- }
-
- getUrl() {
- return this.isRunning ? `http://localhost:${this.port}` : null;
- }
-}
-
-// Export for use in Node.js environment
-if (typeof module !== 'undefined' && module.exports) {
- module.exports = LocalServer;
-}
-
-// Export for use in browser environment
-if (typeof window !== 'undefined') {
- window.LocalServer = LocalServer;
-}
diff --git a/Client-Server/js/LocalServerManager.js b/Client-Server/js/LocalServerManager.js
deleted file mode 100644
index 6347d2c..0000000
--- a/Client-Server/js/LocalServerManager.js
+++ /dev/null
@@ -1,224 +0,0 @@
-/**
- * Local Server Manager
- * Manages the local server for singleplayer mode within the Electron client
- */
-
-class LocalServerManager {
- constructor() {
- this.localServer = null;
- this.isRunning = false;
- this.port = 3004;
- this.startupAttempts = 0;
- this.maxStartupAttempts = 3;
-
- console.log('[LOCAL SERVER MANAGER] LocalServerManager initialized');
- }
-
- async initialize() {
- console.log('[LOCAL SERVER MANAGER] Initializing local server...');
-
- try {
- // In Electron renderer context, use SimpleLocalServer which doesn't require Node.js modules
- if (typeof window !== 'undefined' && window.SimpleLocalServer) {
- this.localServer = new window.SimpleLocalServer();
- console.log('[LOCAL SERVER MANAGER] SimpleLocalServer class loaded from window');
- return true;
- } else if (typeof window !== 'undefined' && window.LocalServer) {
- // Fallback to original LocalServer if available
- this.localServer = new window.LocalServer();
- console.log('[LOCAL SERVER MANAGER] LocalServer class loaded from window');
- return true;
- } else {
- console.warn('[LOCAL SERVER MANAGER] No local server class available');
- return false;
- }
- } catch (error) {
- console.error('[LOCAL SERVER MANAGER] Failed to initialize local server:', error);
- console.log('[LOCAL SERVER MANAGER] Please ensure SimpleLocalServer.js is loaded properly');
- return false;
- }
- }
-
- async startServer() {
- if (this.isRunning) {
- console.log('[LOCAL SERVER MANAGER] Server is already running');
- return { success: true, port: this.port };
- }
-
- if (!this.localServer) {
- const initialized = await this.initialize();
- if (!initialized) {
- return { success: false, error: 'Failed to initialize server' };
- }
- }
-
- console.log(`[LOCAL SERVER MANAGER] Attempting to start server on port ${this.port}`);
-
- try {
- const result = await this.localServer.start(this.port);
-
- if (result.success) {
- this.isRunning = true;
- this.port = result.port;
- this.startupAttempts = 0;
-
- console.log(`[LOCAL SERVER MANAGER] Server started successfully on port ${this.port}`);
- console.log(`[LOCAL SERVER MANAGER] Server URL: ${result.url}`);
-
- // Update LiveMainMenu to use local server
- this.updateMainMenuForLocalMode();
-
- return result;
- } else {
- console.error('[LOCAL SERVER MANAGER] Failed to start server:', result.error);
- this.startupAttempts++;
-
- // Try alternative ports if first attempt fails
- if (this.startupAttempts < this.maxStartupAttempts) {
- this.port = 3004 + this.startupAttempts;
- console.log(`[LOCAL SERVER MANAGER] Retrying with port ${this.port}`);
- return await this.startServer();
- }
-
- return result;
- }
- } catch (error) {
- console.error('[LOCAL SERVER MANAGER] Exception starting server:', error);
- return { success: false, error: error.message };
- }
- }
-
- async stopServer() {
- if (!this.isRunning || !this.localServer) {
- console.log('[LOCAL SERVER MANAGER] Server is not running');
- return { success: true };
- }
-
- console.log('[LOCAL SERVER MANAGER] Stopping local server...');
-
- try {
- const result = await this.localServer.stop();
-
- if (result.success) {
- this.isRunning = false;
- this.port = 3004;
- console.log('[LOCAL SERVER MANAGER] Server stopped successfully');
- } else {
- console.error('[LOCAL SERVER MANAGER] Failed to stop server:', result.error);
- }
-
- return result;
- } catch (error) {
- console.error('[LOCAL SERVER MANAGER] Exception stopping server:', error);
- return { success: false, error: error.message };
- }
- }
-
- getStatus() {
- if (!this.localServer) {
- return {
- isRunning: false,
- port: null,
- connectedClients: 0,
- uptime: 0
- };
- }
-
- const status = this.localServer.getStatus();
- return {
- ...status,
- url: this.isRunning ? `http://localhost:${status.port}` : null
- };
- }
-
- getServerUrl() {
- return this.isRunning ? `http://localhost:${this.port}` : null;
- }
-
- updateMainMenuForLocalMode() {
- // Update LiveMainMenu to use local server URLs
- if (window.liveMainMenu) {
- console.log('[LOCAL SERVER MANAGER] Updating LiveMainMenu for local mode');
-
- window.liveMainMenu.apiBaseUrl = `http://localhost:${this.port}/api`;
- window.liveMainMenu.gameServerUrl = `http://localhost:${this.port}`;
- window.liveMainMenu.isLocalMode = true;
-
- console.log(`[LOCAL SERVER MANAGER] Updated API URL to: ${window.liveMainMenu.apiBaseUrl}`);
- console.log(`[LOCAL SERVER MANAGER] Updated Game Server URL to: ${window.liveMainMenu.gameServerUrl}`);
-
- // Also update GameInitializer URLs
- if (window.gameInitializer) {
- window.gameInitializer.updateServerUrls(
- `http://localhost:${this.port}/api`,
- `http://localhost:${this.port}`
- );
- console.log('[LOCAL SERVER MANAGER] Updated GameInitializer URLs for local mode');
- } else {
- console.warn('[LOCAL SERVER MANAGER] GameInitializer not available for URL update');
- }
- } else {
- console.warn('[LOCAL SERVER MANAGER] LiveMainMenu not available for update');
- }
- }
-
- // Auto-start server when in singleplayer mode
- async autoStartIfSingleplayer() {
- // Check if we should auto-start (no external server available)
- try {
- // Try to connect to external server first
- const response = await fetch('https://api.korvarix.com/health', {
- method: 'GET',
- timeout: 3000
- });
-
- if (response.ok) {
- console.log('[LOCAL SERVER MANAGER] External server available, not starting local server');
- return { success: false, reason: 'External server available' };
- }
- } catch (error) {
- console.log('[LOCAL SERVER MANAGER] External server not available, starting local server');
- }
-
- // Start local server
- return await this.startServer();
- }
-
- // Handle server errors and restart if needed
- handleServerError(error) {
- console.error('[LOCAL SERVER MANAGER] Server error:', error);
-
- // Try to restart server if it crashes
- if (this.isRunning) {
- console.log('[LOCAL SERVER MANAGER] Attempting to restart server...');
- this.stopServer().then(() => {
- setTimeout(() => {
- this.startServer();
- }, 2000); // Wait 2 seconds before restarting
- });
- }
- }
-
- // Get local server info for UI display
- getServerInfo() {
- return {
- isRunning: this.isRunning,
- port: this.port,
- url: this.getServerUrl(),
- status: this.isRunning ? 'Online' : 'Offline',
- mode: 'Local Singleplayer',
- connectedClients: this.localServer ? this.localServer.connectedClients.size : 0,
- uptime: this.localServer ? this.localServer.uptime : 0
- };
- }
-}
-
-// Create global instance
-window.localServerManager = new LocalServerManager();
-
-// Auto-export for module systems
-if (typeof module !== 'undefined' && module.exports) {
- module.exports = LocalServerManager;
-}
-
-console.log('[LOCAL SERVER MANAGER] LocalServerManager loaded and global instance created');
diff --git a/Client-Server/js/SimpleLocalServer.js b/Client-Server/js/SimpleLocalServer.js
deleted file mode 100644
index b5d1215..0000000
--- a/Client-Server/js/SimpleLocalServer.js
+++ /dev/null
@@ -1,535 +0,0 @@
-/**
- * Simple Local Server for Singleplayer Mode
- * A mock server that simulates server responses without requiring Node.js dependencies
- * This runs entirely in the browser/renderer context
- */
-
-class SimpleLocalServer {
- constructor() {
- this.isRunning = false;
- this.port = 3004;
- this.connectedClients = new Map();
- this.existingSaveData = null;
-
- // Check for existing save data on initialization
- this.loadExistingSaveData();
-
- this.mockData = {
- servers: [{
- id: 'local-server',
- name: 'Local Singleplayer',
- description: 'Your personal local server for singleplayer gaming',
- type: 'private',
- region: 'local',
- maxPlayers: 1,
- currentPlayers: 0,
- owner: 'Local Player',
- address: 'localhost',
- port: this.port,
- status: 'online',
- createdAt: new Date().toISOString(),
- ping: 0
- }],
- user: {
- id: 'local-user',
- email: 'local@player.com',
- username: 'Local Player',
- token: 'local-token-' + Date.now(),
- createdAt: new Date().toISOString()
- }
- };
-
- console.log('[SIMPLE LOCAL SERVER] SimpleLocalServer initialized');
- console.log('[SIMPLE LOCAL SERVER] Existing save data found:', !!this.existingSaveData);
- }
-
- // Mock Socket.IO server for local mode
- createMockSocket() {
- console.log('[SIMPLE LOCAL SERVER] Creating mock Socket.IO connection');
-
- const mockSocket = {
- connected: false,
- eventHandlers: {},
-
- on: function(event, handler) {
- console.log(`[MOCKET SOCKET] Registering event handler for: ${event}`);
- if (!this.eventHandlers[event]) {
- this.eventHandlers[event] = [];
- }
- this.eventHandlers[event].push(handler);
- },
-
- emit: function(event, data) {
- console.log(`[MOCKET SOCKET] Emitting event: ${event}`, data);
- },
-
- connect: function() {
- console.log('[MOCKET SOCKET] Connecting...');
- this.connected = true;
-
- // Simulate successful connection
- setTimeout(() => {
- if (this.eventHandlers['connect']) {
- this.eventHandlers['connect'].forEach(handler => handler());
- }
- }, 100);
- },
-
- disconnect: function() {
- console.log('[MOCKET SOCKET] Disconnecting...');
- this.connected = false;
- if (this.eventHandlers['disconnect']) {
- this.eventHandlers['disconnect'].forEach(handler => handler());
- }
- }
- };
-
- // Auto-connect
- mockSocket.connect();
-
- return mockSocket;
- }
-
- loadExistingSaveData() {
- try {
- const saveData = localStorage.getItem(`gso_save_slot_1`);
- if (saveData) {
- this.existingSaveData = JSON.parse(saveData);
- console.log('[SIMPLE LOCAL SERVER] Loaded existing save data:', {
- hasPlayerData: !!this.existingSaveData.player,
- playerLevel: this.existingSaveData.player?.stats?.level,
- lastSave: this.existingSaveData.lastSave,
- gameTime: this.existingSaveData.gameTime
- });
- } else {
- console.log('[SIMPLE LOCAL SERVER] No existing save data found');
- }
- } catch (error) {
- console.warn('[SIMPLE LOCAL SERVER] Error loading existing save data:', error);
- this.existingSaveData = null;
- }
- }
-
- applyExistingSaveDataToGame() {
- if (!this.existingSaveData || !window.game) {
- console.log('[SIMPLE LOCAL SERVER] No existing save data or game not available');
- return false;
- }
-
- try {
- console.log('[SIMPLE LOCAL SERVER] Applying existing save data to game...');
-
- // Apply save data to game systems
- if (this.existingSaveData.player && window.game.systems.player) {
- window.game.systems.player.load(this.existingSaveData.player);
- console.log('[SIMPLE LOCAL SERVER] Player data applied');
- }
-
- if (this.existingSaveData.inventory && window.game.systems.inventory) {
- window.game.systems.inventory.load(this.existingSaveData.inventory);
- console.log('[SIMPLE LOCAL SERVER] Inventory data applied');
- }
-
- if (this.existingSaveData.economy && window.game.systems.economy) {
- window.game.systems.economy.load(this.existingSaveData.economy);
- console.log('[SIMPLE LOCAL SERVER] Economy data applied');
- }
-
- if (this.existingSaveData.idleSystem && window.game.systems.idleSystem) {
- window.game.systems.idleSystem.load(this.existingSaveData.idleSystem);
- console.log('[SIMPLE LOCAL SERVER] Idle system data applied');
- }
-
- if (this.existingSaveData.dungeonSystem && window.game.systems.dungeonSystem) {
- window.game.systems.dungeonSystem.load(this.existingSaveData.dungeonSystem);
- console.log('[SIMPLE LOCAL SERVER] Dungeon system data applied');
- }
-
- if (this.existingSaveData.skillSystem && window.game.systems.skillSystem) {
- window.game.systems.skillSystem.load(this.existingSaveData.skillSystem);
- console.log('[SIMPLE LOCAL SERVER] Skill system data applied');
- }
-
- if (this.existingSaveData.baseSystem && window.game.systems.baseSystem) {
- window.game.systems.baseSystem.load(this.existingSaveData.baseSystem);
- console.log('[SIMPLE LOCAL SERVER] Base system data applied');
- }
-
- if (this.existingSaveData.questSystem && window.game.systems.questSystem) {
- window.game.systems.questSystem.load(this.existingSaveData.questSystem);
- console.log('[SIMPLE LOCAL SERVER] Quest system data applied');
- }
-
- if (this.existingSaveData.gameTime && window.game) {
- window.game.gameTime = this.existingSaveData.gameTime;
- console.log('[SIMPLE LOCAL SERVER] Game time applied:', this.existingSaveData.gameTime);
- }
-
- console.log('[SIMPLE LOCAL SERVER] All save data applied successfully');
- return true;
-
- } catch (error) {
- console.error('[SIMPLE LOCAL SERVER] Error applying save data to game:', error);
- return false;
- }
- }
-
- async start(port = 3004) {
- if (this.isRunning) {
- console.log('[SIMPLE LOCAL SERVER] Server is already running on port', this.port);
- return { success: false, error: 'Server already running' };
- }
-
- try {
- this.port = port;
- this.isRunning = true;
-
- // Update mock server data with actual port
- this.mockData.servers[0].port = port;
-
- console.log(`[SIMPLE LOCAL SERVER] Mock local server started on port ${port}`);
-
- return {
- success: true,
- port: port,
- url: `http://localhost:${port}`
- };
-
- } catch (error) {
- console.error('[SIMPLE LOCAL SERVER] Failed to start server:', error);
- return { success: false, error: error.message };
- }
- }
-
- async stop() {
- if (!this.isRunning) {
- console.log('[SIMPLE LOCAL SERVER] Server is not running');
- return { success: false, error: 'Server is not running' };
- }
-
- try {
- this.isRunning = false;
- this.connectedClients.clear();
-
- console.log('[SIMPLE LOCAL SERVER] Mock local server stopped');
- return { success: true };
-
- } catch (error) {
- console.error('[SIMPLE LOCAL SERVER] Failed to stop server:', error);
- return { success: false, error: error.message };
- }
- }
-
- getStatus() {
- return {
- isRunning: this.isRunning,
- port: this.port,
- connectedClients: this.connectedClients.size,
- uptime: this.isRunning ? 0 : 0 // Mock uptime
- };
- }
-
- getUrl() {
- return this.isRunning ? `http://localhost:${this.port}` : null;
- }
-
- // Mock API methods that simulate server responses
- async mockRequest(method, url, data = null) {
- console.log(`[SIMPLE LOCAL SERVER] Mock ${method} ${url}`, data);
-
- // Simulate network delay
- await new Promise(resolve => setTimeout(resolve, 100));
-
- try {
- if (url === '/health') {
- return {
- status: 200,
- ok: true,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- status: 'OK',
- timestamp: new Date().toISOString(),
- uptime: 0,
- mode: 'local'
- }),
- text: () => Promise.resolve(JSON.stringify({
- status: 'OK',
- timestamp: new Date().toISOString(),
- uptime: 0,
- mode: 'local'
- }))
- };
- }
-
- if (url === '/api/ssc/version') {
- return {
- status: 200,
- ok: true,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- version: '1.0.0',
- service: 'galaxystrikeonline-local-server',
- timestamp: new Date().toISOString(),
- mode: 'local'
- }),
- text: () => Promise.resolve(JSON.stringify({
- version: '1.0.0',
- service: 'galaxystrikeonline-local-server',
- timestamp: new Date().toISOString(),
- mode: 'local'
- }))
- };
- }
-
- if (url === '/api/auth/login' && method === 'POST') {
- return {
- status: 200,
- ok: true,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- success: true,
- user: this.mockData.user,
- token: this.mockData.user.token,
- message: 'Logged in to local mode'
- }),
- text: () => Promise.resolve(JSON.stringify({
- success: true,
- user: this.mockData.user,
- token: this.mockData.user.token,
- message: 'Logged in to local mode'
- }))
- };
- }
-
- if (url === '/api/auth/register' && method === 'POST') {
- return {
- status: 201,
- ok: true,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- success: true,
- user: this.mockData.user,
- token: this.mockData.user.token,
- message: 'Registered in local mode'
- }),
- text: () => Promise.resolve(JSON.stringify({
- success: true,
- user: this.mockData.user,
- token: this.mockData.user.token,
- message: 'Registered in local mode'
- }))
- };
- }
-
- if (url === '/api/servers') {
- return {
- status: 200,
- ok: true,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- success: true,
- servers: this.mockData.servers
- }),
- text: () => Promise.resolve(JSON.stringify({
- success: true,
- servers: this.mockData.servers
- }))
- };
- }
-
- if (url.startsWith('/servers/') && url.endsWith('/join') && method === 'POST') {
- // Mock server join response
- const serverId = url.split('/')[2];
- return {
- status: 200,
- ok: true,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- success: true,
- server: {
- id: serverId,
- name: 'Local Singleplayer',
- address: 'localhost',
- port: this.port,
- gamePort: this.port + 1,
- maxPlayers: 1,
- currentPlayers: 1,
- status: 'online'
- },
- message: 'Joined server successfully'
- }),
- text: () => Promise.resolve(JSON.stringify({
- success: true,
- server: {
- id: serverId,
- name: 'Local Singleplayer',
- address: 'localhost',
- port: this.port,
- gamePort: this.port + 1,
- maxPlayers: 1,
- currentPlayers: 1,
- status: 'online'
- },
- message: 'Joined server successfully'
- }))
- };
- }
-
- if (url.startsWith('/api/game/player/') && method === 'GET') {
- // Return player data from existing save data or localStorage
- let saveData = this.existingSaveData;
-
- // If no existing save data, try localStorage
- if (!saveData) {
- try {
- const localStorageData = localStorage.getItem(`gso_save_slot_1`);
- if (localStorageData) {
- saveData = JSON.parse(localStorageData);
- }
- } catch (error) {
- console.warn('[SIMPLE LOCAL SERVER] Could not access localStorage:', error);
- }
- }
-
- if (saveData) {
- return {
- status: 200,
- ok: true,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- success: true,
- player: saveData.player
- }),
- text: () => Promise.resolve(JSON.stringify({
- success: true,
- player: saveData.player
- }))
- };
- } else {
- return {
- status: 404,
- ok: false,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- success: false,
- error: 'No save data found'
- }),
- text: () => Promise.resolve(JSON.stringify({
- success: false,
- error: 'No save data found'
- }))
- };
- }
- }
-
- if (url.startsWith('/api/game/player/') && method === 'POST') {
- // Save player data to localStorage
- try {
- let existingSaveData = '{}';
-
- if (typeof localStorage !== 'undefined') {
- existingSaveData = localStorage.getItem(`gso_save_slot_1`) || '{}';
- }
-
- const parsedExisting = JSON.parse(existingSaveData);
- parsedExisting.player = data;
- parsedExisting.lastSave = Date.now();
-
- if (typeof localStorage !== 'undefined') {
- localStorage.setItem(`gso_save_slot_1`, JSON.stringify(parsedExisting));
- }
-
- return {
- status: 200,
- ok: true,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- success: true,
- message: 'Player data saved locally'
- }),
- text: () => Promise.resolve(JSON.stringify({
- success: true,
- message: 'Player data saved locally'
- }))
- };
- } catch (error) {
- return {
- status: 500,
- ok: false,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- success: false,
- error: 'Failed to save player data'
- }),
- text: () => Promise.resolve(JSON.stringify({
- success: false,
- error: 'Failed to save player data'
- }))
- };
- }
- }
-
- // Default response for unknown endpoints
- return {
- status: 404,
- ok: false,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- success: false,
- error: 'Endpoint not found'
- }),
- text: () => Promise.resolve(JSON.stringify({
- success: false,
- error: 'Endpoint not found'
- }))
- };
-
- } catch (error) {
- console.error('[SIMPLE LOCAL SERVER] Mock request error:', error);
- return {
- status: 500,
- ok: false,
- headers: {
- get: (name) => name === 'content-type' ? 'application/json' : null
- },
- json: () => Promise.resolve({
- success: false,
- error: 'Internal server error'
- }),
- text: () => Promise.resolve(JSON.stringify({
- success: false,
- error: 'Internal server error'
- }))
- };
- }
- }
-}
-
-// Export for use in browser environment
-if (typeof window !== 'undefined') {
- window.SimpleLocalServer = SimpleLocalServer;
-}
-
-console.log('[SIMPLE LOCAL SERVER] SimpleLocalServer loaded and exported to window');
diff --git a/Client-Server/js/core/Economy.js b/Client-Server/js/core/Economy.js
deleted file mode 100644
index ecdea9c..0000000
--- a/Client-Server/js/core/Economy.js
+++ /dev/null
@@ -1,2335 +0,0 @@
-/**
- * Galaxy Strike Online - Economy System
- * Manages credits, gems, transactions, and shop
- */
-
-class Economy {
- constructor(gameEngine) {
- const debugLogger = window.debugLogger;
- if (debugLogger) debugLogger.log('Economy constructor called');
-
- this.game = gameEngine;
-
- // Currency
- this.credits = 1000;
- this.gems = 10;
- this.premiumCurrency = 0; // For future premium features
-
- // Transaction history
- this.transactionHistory = [];
- this.transactions = []; // Add missing transactions array
-
- // Owned cosmetics
- this.ownedCosmetics = []; // Add missing owned cosmetics array
-
- // Shop categories
- this.shopCategories = {
- ships: 'Ships',
- weapons: 'Weapons',
- armors: 'Armors',
- materials: 'Materials',
- cosmetics: 'Cosmetics',
- // upgrades: 'Upgrades', // Temporarily disabled
- consumables: 'Consumables',
- buildings: 'Buildings'
- };
-
- // Random shop system
- this.randomShopItems = {}; // Current random items per category
- this.shopRefreshInterval = null; // Timer for 2-hour refresh
- this.shopHeartbeatInterval = null; // Timer for live countdown updates
- this.lastShopRefresh = null; // Timestamp of last refresh
- this.SHOP_REFRESH_INTERVAL = 2 * 60 * 60 * 1000; // 2 hours in milliseconds
- this.MAX_ITEMS_PER_CATEGORY = 6;
- this.categoryPurchaseLimits = {}; // Track purchases per category per refresh
-
- // Shop items
- this.shopItems = {
- ships: [
- // Starter Cruiser Variants
- {
- id: 'starter_cruiser_common',
- name: 'Starter Cruiser',
- type: 'ship',
- rarity: 'common',
- price: 5000,
- currency: 'credits',
- description: 'Reliable starter cruiser for new pilots',
- texture: 'assets/textures/ships/starter_cruiser.png',
- stats: { attack: 15, speed: 10, defense: 12, hull: 100 }
- },
- {
- id: 'starter_cruiser_uncommon',
- name: 'Starter Cruiser II',
- type: 'ship',
- rarity: 'uncommon',
- price: 12000,
- currency: 'credits',
- description: 'Upgraded starter cruiser with enhanced systems',
- texture: 'assets/textures/ships/starter_cruiser.png',
- stats: { attack: 18, speed: 12, defense: 15, hull: 120 }
- },
- {
- id: 'starter_cruiser_rare',
- name: 'Starter Cruiser III',
- type: 'ship',
- rarity: 'rare',
- price: 25000,
- currency: 'credits',
- description: 'Elite starter cruiser with advanced weaponry',
- texture: 'assets/textures/ships/starter_cruiser.png',
- stats: { attack: 22, speed: 14, defense: 18, hull: 145 }
- },
- {
- id: 'starter_cruiser_epic',
- name: 'Starter Cruiser IV',
- type: 'ship',
- rarity: 'epic',
- price: 45000,
- currency: 'credits',
- description: 'Master starter cruiser with elite modifications',
- texture: 'assets/textures/ships/starter_cruiser.png',
- stats: { attack: 26, speed: 16, defense: 22, hull: 175 }
- },
- {
- id: 'starter_cruiser_legendary',
- name: 'Starter Cruiser V',
- type: 'ship',
- rarity: 'legendary',
- price: 75000,
- currency: 'credits',
- description: 'Legendary starter cruiser with unparalleled performance',
- texture: 'assets/textures/ships/starter_cruiser.png',
- stats: { attack: 32, speed: 18, defense: 28, hull: 210 }
- },
-
- // Light Destroyer Variants
- {
- id: 'light_destroyer_common',
- name: 'Light Destroyer',
- type: 'ship',
- rarity: 'common',
- price: 12000,
- currency: 'credits',
- description: 'Fast and maneuverable light destroyer',
- texture: 'assets/textures/ships/light_destroyer.png',
- stats: { attack: 18, speed: 18, defense: 10, hull: 80 }
- },
- {
- id: 'light_destroyer_uncommon',
- name: 'Light Destroyer II',
- type: 'ship',
- rarity: 'uncommon',
- price: 28000,
- currency: 'credits',
- description: 'Enhanced light destroyer with improved speed',
- texture: 'assets/textures/ships/light_destroyer.png',
- stats: { attack: 22, speed: 22, defense: 12, hull: 95 }
- },
- {
- id: 'light_destroyer_rare',
- name: 'Light Destroyer III',
- type: 'ship',
- rarity: 'rare',
- price: 55000,
- currency: 'credits',
- description: 'Elite light destroyer with superior agility',
- texture: 'assets/textures/ships/light_destroyer.png',
- stats: { attack: 26, speed: 26, defense: 15, hull: 115 }
- },
- {
- id: 'light_destroyer_epic',
- name: 'Light Destroyer IV',
- type: 'ship',
- rarity: 'epic',
- price: 95000,
- currency: 'credits',
- description: 'Master light destroyer with exceptional speed',
- texture: 'assets/textures/ships/light_destroyer.png',
- stats: { attack: 30, speed: 30, defense: 18, hull: 140 }
- },
- {
- id: 'light_destroyer_legendary',
- name: 'Light Destroyer V',
- type: 'ship',
- rarity: 'legendary',
- price: 150000,
- currency: 'credits',
- description: 'Legendary light destroyer with unmatched velocity',
- texture: 'assets/textures/ships/light_destroyer.png',
- stats: { attack: 36, speed: 36, defense: 22, hull: 170 }
- },
-
- // Heavy Destroyer Variants
- {
- id: 'heavy_destroyer_common',
- name: 'Heavy Destroyer',
- type: 'ship',
- rarity: 'common',
- price: 25000,
- currency: 'credits',
- description: 'Powerful heavy destroyer with strong weapons',
- texture: 'assets/textures/ships/heavy_destroyer.png',
- stats: { attack: 25, speed: 12, defense: 18, hull: 120 }
- },
- {
- id: 'heavy_destroyer_uncommon',
- name: 'Heavy Destroyer II',
- type: 'ship',
- rarity: 'uncommon',
- price: 55000,
- currency: 'credits',
- description: 'Upgraded heavy destroyer with enhanced firepower',
- texture: 'assets/textures/ships/heavy_destroyer.png',
- stats: { attack: 30, speed: 14, defense: 22, hull: 145 }
- },
- {
- id: 'heavy_destroyer_rare',
- name: 'Heavy Destroyer III',
- type: 'ship',
- rarity: 'rare',
- price: 110000,
- currency: 'credits',
- description: 'Elite heavy destroyer with devastating weapons',
- texture: 'assets/textures/ships/heavy_destroyer.png',
- stats: { attack: 35, speed: 16, defense: 26, hull: 175 }
- },
- {
- id: 'heavy_destroyer_epic',
- name: 'Heavy Destroyer IV',
- type: 'ship',
- rarity: 'epic',
- price: 190000,
- currency: 'credits',
- description: 'Master heavy destroyer with overwhelming power',
- texture: 'assets/textures/ships/heavy_destroyer.png',
- stats: { attack: 40, speed: 18, defense: 30, hull: 210 }
- },
- {
- id: 'heavy_destroyer_legendary',
- name: 'Heavy Destroyer V',
- type: 'ship',
- rarity: 'legendary',
- price: 300000,
- currency: 'credits',
- description: 'Legendary heavy destroyer with ultimate destruction',
- texture: 'assets/textures/ships/heavy_destroyer.png',
- stats: { attack: 48, speed: 20, defense: 36, hull: 255 }
- },
-
- // Heavy Cruiser Variants
- {
- id: 'heavy_cruiser_common',
- name: 'Heavy Cruiser',
- type: 'ship',
- rarity: 'common',
- price: 45000,
- currency: 'credits',
- description: 'Massive heavy cruiser with excellent defense',
- texture: 'assets/textures/ships/heavy_cruiser.png',
- stats: { attack: 22, speed: 8, defense: 25, hull: 150 }
- },
- {
- id: 'heavy_cruiser_uncommon',
- name: 'Heavy Cruiser II',
- type: 'ship',
- rarity: 'uncommon',
- price: 95000,
- currency: 'credits',
- description: 'Enhanced heavy cruiser with superior armor',
- texture: 'assets/textures/ships/heavy_cruiser.png',
- stats: { attack: 26, speed: 9, defense: 30, hull: 180 }
- },
- {
- id: 'heavy_cruiser_rare',
- name: 'Heavy Cruiser III',
- type: 'ship',
- rarity: 'rare',
- price: 190000,
- currency: 'credits',
- description: 'Elite heavy cruiser with fortress-like defense',
- texture: 'assets/textures/ships/heavy_cruiser.png',
- stats: { attack: 30, speed: 10, defense: 36, hull: 220 }
- },
- {
- id: 'heavy_cruiser_epic',
- name: 'Heavy Cruiser IV',
- type: 'ship',
- rarity: 'epic',
- price: 320000,
- currency: 'credits',
- description: 'Master heavy cruiser with impenetrable armor',
- texture: 'assets/textures/ships/heavy_cruiser.png',
- stats: { attack: 34, speed: 11, defense: 42, hull: 265 }
- },
- {
- id: 'heavy_cruiser_legendary',
- name: 'Heavy Cruiser V',
- type: 'ship',
- rarity: 'legendary',
- price: 500000,
- currency: 'credits',
- description: 'Legendary heavy cruiser with ultimate defense',
- texture: 'assets/textures/ships/heavy_cruiser.png',
- stats: { attack: 40, speed: 12, defense: 50, hull: 320 }
- }
- ],
- weapons: [
- // Starter Blaster Variants
- {
- id: 'starter_blaster_common',
- name: 'Starter Blaster',
- type: 'weapon',
- rarity: 'common',
- price: 2000,
- currency: 'credits',
- description: 'Basic blaster for new pilots',
- texture: 'assets/textures/weapons/starter_blaster.png',
- stats: { damage: 10, fireRate: 2, range: 5, energy: 5 }
- },
- {
- id: 'starter_blaster_uncommon',
- name: 'Starter Blaster II',
- type: 'weapon',
- rarity: 'uncommon',
- price: 5000,
- currency: 'credits',
- description: 'Enhanced starter blaster with better fire rate',
- texture: 'assets/textures/weapons/starter_blaster.png',
- stats: { damage: 12, fireRate: 2.5, range: 5.5, energy: 6 }
- },
- {
- id: 'starter_blaster_rare',
- name: 'Starter Blaster III',
- type: 'weapon',
- rarity: 'rare',
- price: 12000,
- currency: 'credits',
- description: 'Elite starter blaster with improved damage',
- texture: 'assets/textures/weapons/starter_blaster.png',
- stats: { damage: 15, fireRate: 3, range: 6, energy: 7 }
- },
- {
- id: 'starter_blaster_epic',
- name: 'Starter Blaster IV',
- type: 'weapon',
- rarity: 'epic',
- price: 25000,
- currency: 'credits',
- description: 'Master starter blaster with superior performance',
- texture: 'assets/textures/weapons/starter_blaster.png',
- stats: { damage: 18, fireRate: 3.5, range: 6.5, energy: 8 }
- },
- {
- id: 'starter_blaster_legendary',
- name: 'Starter Blaster V',
- type: 'weapon',
- rarity: 'legendary',
- price: 50000,
- currency: 'credits',
- description: 'Legendary starter blaster with ultimate power',
- texture: 'assets/textures/weapons/starter_blaster.png',
- stats: { damage: 22, fireRate: 4, range: 7, energy: 10 }
- },
-
- // Laser Pistol Variants
- {
- id: 'laser_pistol_common',
- name: 'Laser Pistol',
- type: 'weapon',
- rarity: 'common',
- price: 3000,
- currency: 'credits',
- description: 'Compact laser pistol for close combat',
- texture: 'assets/textures/weapons/laser_pistol.png',
- stats: { damage: 8, fireRate: 3, range: 4, energy: 3 }
- },
- {
- id: 'laser_pistol_uncommon',
- name: 'Laser Pistol II',
- type: 'weapon',
- rarity: 'uncommon',
- price: 7500,
- currency: 'credits',
- description: 'Enhanced laser pistol with better accuracy',
- texture: 'assets/textures/weapons/laser_pistol.png',
- stats: { damage: 10, fireRate: 3.5, range: 4.5, energy: 4 }
- },
- {
- id: 'laser_pistol_rare',
- name: 'Laser Pistol III',
- type: 'weapon',
- rarity: 'rare',
- price: 18000,
- currency: 'credits',
- description: 'Elite laser pistol with piercing shots',
- texture: 'assets/textures/weapons/laser_pistol.png',
- stats: { damage: 13, fireRate: 4, range: 5, energy: 5 }
- },
- {
- id: 'laser_pistol_epic',
- name: 'Laser Pistol IV',
- type: 'weapon',
- rarity: 'epic',
- price: 35000,
- currency: 'credits',
- description: 'Master laser pistol with rapid fire capability',
- texture: 'assets/textures/weapons/laser_pistol.png',
- stats: { damage: 16, fireRate: 4.5, range: 5.5, energy: 6 }
- },
- {
- id: 'laser_pistol_legendary',
- name: 'Laser Pistol V',
- type: 'weapon',
- rarity: 'legendary',
- price: 70000,
- currency: 'credits',
- description: 'Legendary laser pistol with devastating power',
- texture: 'assets/textures/weapons/laser_pistol.png',
- stats: { damage: 20, fireRate: 5, range: 6, energy: 8 }
- },
-
- // Laser Sniper Rifle Variants
- {
- id: 'laser_sniper_rifle_common',
- name: 'Laser Sniper Rifle',
- type: 'weapon',
- rarity: 'common',
- price: 8000,
- currency: 'credits',
- description: 'Long-range laser sniper rifle for precision attacks',
- texture: 'assets/textures/weapons/laser_sniper_rifle.png',
- stats: { damage: 25, fireRate: 1, range: 10, energy: 8 }
- },
- {
- id: 'laser_sniper_rifle_uncommon',
- name: 'Laser Sniper Rifle II',
- type: 'weapon',
- rarity: 'uncommon',
- price: 20000,
- currency: 'credits',
- description: 'Enhanced laser sniper rifle with better penetration',
- texture: 'assets/textures/weapons/laser_sniper_rifle.png',
- stats: { damage: 30, fireRate: 1.2, range: 11, energy: 9 }
- },
- {
- id: 'laser_sniper_rifle_rare',
- name: 'Laser Sniper Rifle III',
- type: 'weapon',
- rarity: 'rare',
- price: 50000,
- currency: 'credits',
- description: 'Elite laser sniper rifle with deadly accuracy',
- texture: 'assets/textures/weapons/laser_sniper_rifle.png',
- stats: { damage: 36, fireRate: 1.4, range: 12, energy: 10 }
- },
- {
- id: 'laser_sniper_rifle_epic',
- name: 'Laser Sniper Rifle IV',
- type: 'weapon',
- rarity: 'epic',
- price: 100000,
- currency: 'credits',
- description: 'Master laser sniper rifle with extreme range',
- texture: 'assets/textures/weapons/laser_sniper_rifle.png',
- stats: { damage: 42, fireRate: 1.6, range: 13, energy: 12 }
- },
- {
- id: 'laser_sniper_rifle_legendary',
- name: 'Laser Sniper Rifle V',
- type: 'weapon',
- rarity: 'legendary',
- price: 200000,
- currency: 'credits',
- description: 'Legendary laser sniper rifle with unparalleled precision',
- texture: 'assets/textures/weapons/laser_sniper_rifle.png',
- stats: { damage: 50, fireRate: 2, range: 15, energy: 15 }
- }
- ],
- armors: [
- // Basic Armor Variants
- {
- id: 'basic_armor_common',
- name: 'Basic Armor',
- type: 'armor',
- rarity: 'common',
- price: 1500,
- currency: 'credits',
- description: 'Light protection for beginners',
- texture: 'assets/textures/armors/basic_armor.png',
- stats: { defense: 5, durability: 20, weight: 2, energyShield: 0 }
- },
- {
- id: 'basic_armor_uncommon',
- name: 'Basic Armor II',
- type: 'armor',
- rarity: 'uncommon',
- price: 4000,
- currency: 'credits',
- description: 'Improved basic armor with better durability',
- texture: 'assets/textures/armors/basic_armor.png',
- stats: { defense: 7, durability: 25, weight: 2.2, energyShield: 2 }
- },
- {
- id: 'basic_armor_rare',
- name: 'Basic Armor III',
- type: 'armor',
- rarity: 'rare',
- price: 10000,
- currency: 'credits',
- description: 'Elite basic armor with energy shielding',
- texture: 'assets/textures/armors/basic_armor.png',
- stats: { defense: 10, durability: 30, weight: 2.5, energyShield: 5 }
- },
- {
- id: 'basic_armor_epic',
- name: 'Basic Armor IV',
- type: 'armor',
- rarity: 'epic',
- price: 20000,
- currency: 'credits',
- description: 'Master basic armor with advanced protection',
- texture: 'assets/textures/armors/basic_armor.png',
- stats: { defense: 13, durability: 35, weight: 2.8, energyShield: 8 }
- },
- {
- id: 'basic_armor_legendary',
- name: 'Basic Armor V',
- type: 'armor',
- rarity: 'legendary',
- price: 40000,
- currency: 'credits',
- description: 'Legendary basic armor with ultimate defense',
- texture: 'assets/textures/armors/basic_armor.png',
- stats: { defense: 17, durability: 40, weight: 3, energyShield: 12 }
- },
-
- // Medium Armor Variants
- {
- id: 'medium_armor_common',
- name: 'Medium Armor',
- type: 'armor',
- rarity: 'common',
- price: 5000,
- currency: 'credits',
- description: 'Balanced armor for general combat',
- texture: 'assets/textures/armors/medium_armor.png',
- stats: { defense: 12, durability: 40, weight: 5, energyShield: 3 }
- },
- {
- id: 'medium_armor_uncommon',
- name: 'Medium Armor II',
- type: 'armor',
- rarity: 'uncommon',
- price: 12000,
- currency: 'credits',
- description: 'Enhanced medium armor with better shielding',
- texture: 'assets/textures/armors/medium_armor.png',
- stats: { defense: 15, durability: 50, weight: 5.5, energyShield: 6 }
- },
- {
- id: 'medium_armor_rare',
- name: 'Medium Armor III',
- type: 'armor',
- rarity: 'rare',
- price: 30000,
- currency: 'credits',
- description: 'Elite medium armor with advanced systems',
- texture: 'assets/textures/armors/medium_armor.png',
- stats: { defense: 19, durability: 60, weight: 6, energyShield: 10 }
- },
- {
- id: 'medium_armor_epic',
- name: 'Medium Armor IV',
- type: 'armor',
- rarity: 'epic',
- price: 60000,
- currency: 'credits',
- description: 'Master medium armor with superior protection',
- texture: 'assets/textures/armors/medium_armor.png',
- stats: { defense: 23, durability: 70, weight: 6.5, energyShield: 15 }
- },
- {
- id: 'medium_armor_legendary',
- name: 'Medium Armor V',
- type: 'armor',
- rarity: 'legendary',
- price: 100000,
- currency: 'credits',
- description: 'Legendary medium armor with ultimate defense',
- texture: 'assets/textures/armors/medium_armor.png',
- stats: { defense: 28, durability: 80, weight: 7, energyShield: 20 }
- },
-
- // Heavy Armor Variants
- {
- id: 'heavy_armor_common',
- name: 'Heavy Armor',
- type: 'armor',
- rarity: 'common',
- price: 10000,
- currency: 'credits',
- description: 'Maximum protection with increased weight',
- texture: 'assets/textures/armors/heavy_armor.png',
- stats: { defense: 20, durability: 60, weight: 8, energyShield: 5 }
- },
- {
- id: 'heavy_armor_uncommon',
- name: 'Heavy Armor II',
- type: 'armor',
- rarity: 'uncommon',
- price: 25000,
- currency: 'credits',
- description: 'Upgraded heavy armor with better energy shielding',
- texture: 'assets/textures/armors/heavy_armor.png',
- stats: { defense: 25, durability: 75, weight: 9, energyShield: 10 }
- },
- {
- id: 'heavy_armor_rare',
- name: 'Heavy Armor III',
- type: 'armor',
- rarity: 'rare',
- price: 60000,
- currency: 'credits',
- description: 'Elite heavy armor with advanced protection systems',
- texture: 'assets/textures/armors/heavy_armor.png',
- stats: { defense: 31, durability: 90, weight: 10, energyShield: 16 }
- },
- {
- id: 'heavy_armor_epic',
- name: 'Heavy Armor IV',
- type: 'armor',
- rarity: 'epic',
- price: 120000,
- currency: 'credits',
- description: 'Master heavy armor with superior defense capabilities',
- texture: 'assets/textures/armors/heavy_armor.png',
- stats: { defense: 37, durability: 105, weight: 11, energyShield: 23 }
- },
- {
- id: 'heavy_armor_legendary',
- name: 'Heavy Armor V',
- type: 'armor',
- rarity: 'legendary',
- price: 200000,
- currency: 'credits',
- description: 'Legendary heavy armor with ultimate protection',
- texture: 'assets/textures/armors/heavy_armor.png',
- stats: { defense: 45, durability: 120, weight: 12, energyShield: 30 }
- }
- ],
- cosmetics: [
- {
- id: 'blue_paint',
- name: 'Blue Paint Job',
- type: 'cosmetic',
- rarity: 'common',
- price: 100,
- currency: 'gems',
- description: 'Custom blue paint for your ship'
- },
- {
- id: 'golden_trim',
- name: 'Golden Trim',
- type: 'cosmetic',
- rarity: 'rare',
- price: 500,
- currency: 'gems',
- description: 'Luxurious golden trim for your ship'
- }
- ],
- consumables: [
- {
- id: 'health_kit',
- name: 'Health Kit',
- type: 'consumable',
- rarity: 'common',
- price: 15,
- currency: 'credits',
- description: 'Restores 50 health points',
- texture: 'assets/textures/items/health_pack.png',
- effect: { heal: 50 }
- },
- {
- id: 'mega_health_kit',
- name: 'Mega Health Kit',
- type: 'consumable',
- rarity: 'uncommon',
- price: 50,
- currency: 'credits',
- description: 'Restores full health',
- texture: 'assets/textures/items/mega_health_pack.png',
- effect: { heal: 999 }
- }
- ],
- buildings: [
- {
- id: 'command_center',
- name: 'Command Center',
- type: 'building',
- rarity: 'uncommon',
- price: 50000,
- currency: 'credits',
- description: 'Central command facility for base operations and coordination',
- texture: 'assets/textures/base/command_center.png',
- stats: { command: 10, efficiency: 15, capacity: 20 }
- },
- {
- id: 'mining_facility',
- name: 'Mining Facility',
- type: 'building',
- rarity: 'common',
- price: 25000,
- currency: 'credits',
- description: 'Automated mining facility for resource extraction and processing',
- texture: 'assets/textures/base/mining_facility.png',
- stats: { production: 12, efficiency: 8, storage: 15 }
- }
- ],
- materials: [
- {
- id: 'iron_ore',
- name: 'Iron Ore',
- type: 'material',
- rarity: 'common',
- price: 10,
- currency: 'credits',
- description: 'Basic metal ore used for crafting weapons and armor',
- texture: 'assets/textures/items/iron_ore.png',
- stackable: true
- },
- {
- id: 'copper_ore',
- name: 'Copper Ore',
- type: 'material',
- rarity: 'common',
- price: 8,
- currency: 'credits',
- description: 'Conductive metal ore used for wiring and electronics',
- texture: 'assets/textures/items/copper_ore.png',
- stackable: true
- },
- {
- id: 'tin_bar',
- name: 'Tin Bar',
- type: 'material',
- rarity: 'common',
- price: 12,
- currency: 'credits',
- description: 'Refined tin bar used for alloys and soldering',
- texture: 'assets/textures/items/tin_bar.png',
- stackable: true
- },
- {
- id: 'copper_wire',
- name: 'Copper Wire',
- type: 'material',
- rarity: 'common',
- price: 8,
- currency: 'credits',
- description: 'Conductive wiring for electronic components',
- texture: 'assets/textures/items/copper_wire.png',
- stackable: true
- },
- {
- id: 'energy_crystal',
- name: 'Energy Crystal',
- type: 'material',
- rarity: 'uncommon',
- price: 25,
- currency: 'credits',
- description: 'Crystallized energy source for power systems',
- texture: 'assets/textures/items/energy_crystal.png',
- stackable: true
- },
- {
- id: 'leather',
- name: 'Leather',
- type: 'material',
- rarity: 'common',
- price: 12,
- currency: 'credits',
- description: 'Durable material for crafting armor and accessories',
- texture: 'assets/textures/items/leather.png',
- stackable: true
- },
- {
- id: 'herbs',
- name: 'Herbs',
- type: 'material',
- rarity: 'common',
- price: 5,
- currency: 'credits',
- description: 'Medicinal herbs used for healing items',
- texture: 'assets/textures/items/herbs.png',
- stackable: true
- },
- {
- id: 'bandages',
- name: 'Bandages',
- type: 'material',
- rarity: 'common',
- price: 3,
- currency: 'credits',
- description: 'Medical bandages used for crafting healing items',
- texture: 'assets/textures/items/bandages.png',
- stackable: true
- },
- {
- id: 'steel_plate',
- name: 'Steel Plate',
- type: 'material',
- rarity: 'uncommon',
- price: 30,
- currency: 'credits',
- description: 'Reinforced steel plates used for advanced armor and ship components',
- texture: 'assets/textures/items/stell_plate.png',
- stackable: true
- },
- {
- id: 'advanced_component',
- name: 'Advanced Component',
- type: 'material',
- rarity: 'rare',
- price: 75,
- currency: 'credits',
- description: 'High-tech component used for advanced equipment and upgrades',
- texture: 'assets/textures/items/advanced_component.png',
- stackable: true
- },
- {
- id: 'advanced_components',
- name: 'Advanced Components Set',
- type: 'material',
- rarity: 'epic',
- price: 150,
- currency: 'credits',
- description: 'Complete set of advanced components for high-end manufacturing',
- texture: 'assets/textures/items/advanced_components.png',
- stackable: true
- },
- {
- id: 'basic_circuitboard',
- name: 'Basic Circuit Board',
- type: 'material',
- rarity: 'common',
- price: 20,
- currency: 'credits',
- description: 'Basic electronic circuit board for simple devices',
- texture: 'assets/textures/items/basic_circuitboard.png',
- stackable: true
- },
- {
- id: 'common_circuitboard',
- name: 'Common Circuit Board',
- type: 'material',
- rarity: 'common',
- price: 35,
- currency: 'credits',
- description: 'Standard circuit board for most electronic equipment',
- texture: 'assets/textures/items/common_circuitboard.png',
- stackable: true
- },
- {
- id: 'advanced_circuitboard',
- name: 'Advanced Circuit Board',
- type: 'material',
- rarity: 'rare',
- price: 100,
- currency: 'credits',
- description: 'High-performance circuit board for advanced systems',
- texture: 'assets/textures/items/advanced_circuitboard.png',
- stackable: true
- },
- {
- id: 'battery',
- name: 'Battery',
- type: 'material',
- rarity: 'uncommon',
- price: 35,
- currency: 'credits',
- description: 'Power batteries used for energy-based equipment',
- texture: 'assets/textures/items/battery.png',
- stackable: true
- },
- {
- id: 'advanced_components',
- name: 'Advanced Components',
- type: 'material',
- rarity: 'rare',
- price: 150,
- currency: 'credits',
- description: 'Sophisticated electronic components for advanced ship systems',
- stackable: true
- }
- ]
- };
-
- // Owned cosmetics
- this.ownedCosmetics = [];
-
- if (debugLogger) debugLogger.log('Economy constructor completed', {
- initialCredits: this.credits,
- initialGems: this.gems,
- shopCategoriesCount: Object.keys(this.shopCategories).length,
- totalShopItems: Object.values(this.shopItems).reduce((total, category) => total + category.length, 0)
- });
- }
-
- async initialize() {
- const debugLogger = window.debugLogger;
- console.log('[ECONOMY] Economy system initializing');
- if (debugLogger) await debugLogger.startStep('economyInitialize');
-
- try {
- if (debugLogger) await debugLogger.logStep('Economy initialization started', {
- currentCredits: this.credits,
- currentGems: this.gems,
- transactionHistoryLength: this.transactionHistory.length
- });
-
- // Setup shop purchase event listeners
- this.setupShopEventListeners();
-
- // Initialize random shop system
- this.initializeRandomShop();
-
- console.log('[ECONOMY] Economy system initialization completed');
- if (debugLogger) await debugLogger.endStep('economyInitialize');
- } catch (error) {
- console.error('[ECONOMY] Error during initialization:', error);
- if (debugLogger) await debugLogger.errorEvent(error, 'Economy Initialize');
- }
- }
-
- initializeRandomShop() {
- const debugLogger = window.debugLogger;
- console.log('[ECONOMY] Initializing random shop system');
-
- // Shop data is now loaded through the main save/load system in load()
- // Only initialize if no shop data exists (new game)
- if (!this.lastShopRefresh || Object.keys(this.randomShopItems).length === 0) {
- console.log('[ECONOMY] No shop data found, generating new shop');
- this.refreshRandomShop();
- } else {
- console.log('[ECONOMY] Shop data already loaded from save');
- // Start the refresh timer for ongoing updates
- this.startShopRefreshTimer();
- }
-
- if (debugLogger) debugLogger.logStep('Random shop system initialized', {
- hasShopData: Object.keys(this.randomShopItems).length > 0,
- lastRefresh: this.lastShopRefresh
- });
- }
-
- refreshRandomShop() {
- const debugLogger = window.debugLogger;
- console.log('[ECONOMY] Refreshing random shop items');
-
- const categories = Object.keys(this.shopCategories);
- this.randomShopItems = {};
-
- categories.forEach(category => {
- const categoryItems = this.shopItems[category] || [];
- if (categoryItems.length === 0) return;
-
- // Group items by their base ID (remove tier suffixes)
- const baseItemGroups = {};
- categoryItems.forEach(item => {
- // Extract base ID by removing tier suffixes (common, uncommon, rare, epic, legendary)
- const baseId = item.id.replace(/_(common|uncommon|rare|epic|legendary)$/, '');
- if (!baseItemGroups[baseId]) {
- baseItemGroups[baseId] = [];
- }
- baseItemGroups[baseId].push(item);
- });
-
- // Get all unique base items
- const uniqueBaseItems = Object.keys(baseItemGroups);
-
- // Randomly select up to MAX_ITEMS_PER_CATEGORY base items
- const shuffledBaseItems = [...uniqueBaseItems].sort(() => Math.random() - 0.5);
- const selectedBaseItems = shuffledBaseItems.slice(0, Math.min(this.MAX_ITEMS_PER_CATEGORY, shuffledBaseItems.length));
-
- // For each selected base item, randomly pick one tier variant
- this.randomShopItems[category] = selectedBaseItems.map(baseId => {
- const variants = baseItemGroups[baseId];
- const selectedVariant = variants[Math.floor(Math.random() * variants.length)];
-
- return {
- ...selectedVariant,
- id: `${selectedVariant.id}_random_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
- originalId: selectedVariant.id,
- baseId: baseId,
- price: Math.round(selectedVariant.price * (0.8 + Math.random() * 0.4)), // ±20% price variation
- isRandomItem: true,
- refreshTimestamp: Date.now()
- };
- });
-
- // Reset purchase limits for this category
- this.categoryPurchaseLimits[category] = 0;
-
- if (debugLogger) debugLogger.logStep('Random shop category refreshed', {
- category: category,
- totalBaseItems: uniqueBaseItems.length,
- baseItemsSelected: selectedBaseItems.length,
- itemsGenerated: this.randomShopItems[category].length,
- selectedVariants: this.randomShopItems[category].map(item => ({
- baseId: item.baseId,
- variantId: item.originalId,
- rarity: item.rarity,
- name: item.name
- })),
- purchaseLimitReset: true
- });
- });
-
- this.lastShopRefresh = Date.now();
- this.saveRandomShopData();
- this.startShopRefreshTimer();
-
- // Update UI if shop tab is active
- this.updateShopUI();
-
- this.game.showNotification('Shop inventory refreshed!', 'info', 3000);
-
- if (debugLogger) debugLogger.logStep('Random shop refresh completed', {
- categoriesRefreshed: categories.length,
- totalItemsGenerated: Object.values(this.randomShopItems).flat().length,
- nextRefresh: new Date(this.lastShopRefresh + this.SHOP_REFRESH_INTERVAL)
- });
- }
-
- startShopRefreshTimer() {
- // Clear existing timers
- if (this.shopRefreshInterval) {
- clearInterval(this.shopRefreshInterval);
- }
- if (this.shopHeartbeatInterval) {
- clearInterval(this.shopHeartbeatInterval);
- }
-
- // Set up heartbeat timer for live countdown updates (every second)
- this.shopHeartbeatInterval = setInterval(() => {
- this.updateShopCountdown();
- }, 1000);
-
- // Set up refresh timer (every 2 hours)
- this.shopRefreshInterval = setInterval(() => {
- this.refreshRandomShop();
- }, this.SHOP_REFRESH_INTERVAL);
-
- console.log('[ECONOMY] Shop refresh timers started');
- }
-
- updateShopCountdown() {
- // Only update if shop tab is active and using random shop
- const shopTab = document.getElementById('shop-tab');
- if (!shopTab || shopTab.style.display === 'none') {
- return;
- }
-
- const activeCategory = document.querySelector('.shop-cat-btn.active')?.dataset.category || 'ships';
- if (!this.randomShopItems[activeCategory]) {
- return;
- }
-
- // Find and update the countdown element
- const countdownElement = document.querySelector('.refresh-countdown');
- if (countdownElement) {
- countdownElement.innerHTML = `
-
- Next refresh in: ${this.getShopRefreshCountdown()}
- `;
- }
- }
-
- stopShopRefreshTimer() {
- if (this.shopRefreshInterval) {
- clearInterval(this.shopRefreshInterval);
- this.shopRefreshInterval = null;
- }
- if (this.shopHeartbeatInterval) {
- clearInterval(this.shopHeartbeatInterval);
- this.shopHeartbeatInterval = null;
- }
- console.log('[ECONOMY] Shop refresh timers stopped');
- }
-
- loadRandomShopData() {
- try {
- const savedData = localStorage.getItem('gso_random_shop');
- if (savedData) {
- const data = JSON.parse(savedData);
- this.randomShopItems = data.randomShopItems || {};
- this.categoryPurchaseLimits = data.categoryPurchaseLimits || {};
- this.lastShopRefresh = data.lastShopRefresh || null;
- console.log('[ECONOMY] Random shop data loaded from localStorage');
- }
- } catch (error) {
- console.error('[ECONOMY] Error loading random shop data:', error);
- this.randomShopItems = {};
- this.categoryPurchaseLimits = {};
- this.lastShopRefresh = null;
- }
- }
- saveRandomShopData() {
- try {
- const data = {
- randomShopItems: this.randomShopItems,
- categoryPurchaseLimits: this.categoryPurchaseLimits,
- lastShopRefresh: this.lastShopRefresh
- };
- localStorage.setItem('gso_random_shop', JSON.stringify(data));
- console.log('[ECONOMY] Random shop data saved to localStorage');
- } catch (error) {
- console.error('[ECONOMY] Error saving random shop data:', error);
- }
- }
-
- shouldRefreshShop() {
- // Check if enough time has passed since the last actual refresh
- if (!this.lastShopRefresh) {
- return true; // No previous refresh, need to refresh
- }
-
- const now = Date.now();
- const timeSinceRefresh = now - this.lastShopRefresh;
- return timeSinceRefresh >= this.SHOP_REFRESH_INTERVAL;
- }
-
- getShopRefreshCountdown() {
- const schedule = this.getRefreshSchedule();
- const now = Date.now();
-
- // Calculate time until next server refresh
- const timeUntilRefresh = schedule.nextRefresh.getTime() - now;
-
- if (timeUntilRefresh <= 0) {
- return 'Refreshing...';
- }
-
- return this.formatTimeRemaining(timeUntilRefresh);
- }
-
- getNextRefreshTime() {
- // Calculate the next refresh time based on server time
- const now = Date.now();
-
- if (!this.lastShopRefresh) {
- // If no last refresh, next refresh is now + interval
- return new Date(now + this.SHOP_REFRESH_INTERVAL);
- }
-
- // Calculate next refresh time
- const timeSinceRefresh = now - this.lastShopRefresh;
- const intervalsPassed = Math.floor(timeSinceRefresh / this.SHOP_REFRESH_INTERVAL);
- const nextRefreshTime = this.lastShopRefresh + ((intervalsPassed + 1) * this.SHOP_REFRESH_INTERVAL);
-
- return new Date(nextRefreshTime);
- }
-
- getRefreshSchedule() {
- // Generate a consistent refresh schedule based on server time
- const now = new Date();
- const currentHour = now.getHours();
-
- // Refresh at even hours: 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22
- let nextRefreshHour;
-
- if (currentHour % 2 === 0 && now.getMinutes() === 0 && now.getSeconds() === 0) {
- // Exactly on an even hour at :00:00, next refresh is in 2 hours
- nextRefreshHour = currentHour + 2;
- } else {
- // Find the next even hour
- nextRefreshHour = currentHour + (2 - (currentHour % 2));
- }
-
- // Handle midnight wrap-around
- if (nextRefreshHour >= 24) {
- nextRefreshHour = nextRefreshHour % 24;
- }
-
- const nextRefresh = new Date(now);
- nextRefresh.setHours(nextRefreshHour, 0, 0, 0);
-
- // If the calculated time is in the past (shouldn't happen but just in case), add 2 hours
- if (nextRefresh <= now) {
- nextRefresh.setHours(nextRefresh.getHours() + 2);
- }
-
- console.log('[ECONOMY] Current time:', now.toLocaleTimeString());
- console.log('[ECONOMY] Next refresh at:', nextRefresh.toLocaleTimeString());
-
- return {
- nextRefresh: nextRefresh,
- interval: 2 * 60 * 60 * 1000, // 2 hours
- schedule: 'Every 2 hours at even hours (2,4,6,8,10,12,14,16,18,20,22,00)'
- };
- }
-
- formatTimeRemaining(timeUntilRefresh) {
- const hours = Math.floor(timeUntilRefresh / (1000 * 60 * 60));
- const minutes = Math.floor((timeUntilRefresh % (1000 * 60 * 60)) / (1000 * 60));
- const seconds = Math.floor((timeUntilRefresh % (1000 * 60)) / 1000);
-
- if (hours > 0) {
- return `${hours}h ${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
- } else {
- return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
- }
- }
-
- getItemCategory(item) {
- // Determine category based on item type
- switch (item.type) {
- case 'ship': return 'ships';
- case 'weapon': return 'weapons';
- case 'armor': return 'armors';
- case 'material': return 'materials';
- case 'cosmetic': return 'cosmetics';
- case 'upgrade': return 'upgrades';
- case 'consumable': return 'consumables';
- case 'building': return 'buildings';
- default:
- // For random items, check if we can find them in shop categories
- for (const [category, items] of Object.entries(this.shopItems)) {
- if (items.some(shopItem => shopItem.id === item.originalId || shopItem.id === item.id)) {
- return category;
- }
- }
- return 'unknown';
- }
- }
-
- setupShopEventListeners() {
- const debugLogger = window.debugLogger;
- console.log('[ECONOMY] Setting up shop event listeners');
-
- // Remove existing listeners to prevent duplicates
- const shopItemsElement = document.getElementById('shopItems');
- if (shopItemsElement) {
- // Clone the element to remove all event listeners
- const newElement = shopItemsElement.cloneNode(true);
- shopItemsElement.parentNode.replaceChild(newElement, shopItemsElement);
-
- // Setup shop purchase button event delegation on the new element
- newElement.addEventListener('click', (event) => {
- const purchaseBtn = event.target.closest('.shop-item-purchase-btn');
- if (purchaseBtn && !purchaseBtn.disabled) {
- const itemId = purchaseBtn.getAttribute('data-item-id');
- console.log(`[ECONOMY] Shop purchase button clicked for item: ${itemId}`);
-
- if (itemId) {
- this.purchaseItem(itemId, 1);
- } else {
- console.error('[ECONOMY] No item ID found on purchase button');
- }
- }
- });
-
- console.log('[ECONOMY] Shop event listeners setup completed');
- } else {
- console.error('[ECONOMY] Shop items element not found');
- }
- }
-
- // Currency management
- addCredits(amount, source = 'unknown') {
- const oldCredits = this.credits;
- this.credits += amount;
- this.addTransaction('credits', amount, source);
-
- console.log(`[ECONOMY] Added ${amount} credits from ${source}. New total: ${this.credits}`);
-
- // Update UI to show new credit amount
- if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
- this.game.systems.ui.updateUI();
- }
-
- this.game.showNotification(`+${this.game.formatNumber(amount)} credits`, 'success', 2000);
- }
-
- removeCredits(amount) {
- const oldCredits = this.credits;
-
- if (this.credits < amount) {
- this.game.showNotification('Not enough credits!', 'error', 3000);
- return false;
- }
-
- this.credits -= amount;
- this.addTransaction('credits', -amount, 'purchase');
-
- console.log(`[ECONOMY] Removed ${amount} credits. New total: ${this.credits}`);
-
- // Update UI to show new credit amount
- if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
- this.game.systems.ui.updateUI();
- }
-
- return true;
- }
-
- addGems(amount, source = 'unknown') {
- const oldGems = this.gems;
- this.gems += amount;
- this.addTransaction('gems', amount, source);
-
- console.log(`[ECONOMY] Added ${amount} gems from ${source}. New total: ${this.gems}`);
-
- // Update UI to show new gem amount
- if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
- this.game.systems.ui.updateUI();
- }
-
- this.game.showNotification(`+${this.game.formatNumber(amount)} gems`, 'success', 2000);
- }
-
- removeGems(amount) {
- const oldGems = this.gems;
-
- if (this.gems < amount) {
- this.game.showNotification('Not enough gems!', 'error', 3000);
- return false;
- }
-
- this.gems -= amount;
- this.addTransaction('gems', -amount, 'purchase');
-
- console.log(`[ECONOMY] Removed ${amount} gems. New total: ${this.gems}`);
-
- // Update UI to show new gem amount
- if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
- this.game.systems.ui.updateUI();
- }
-
- return true;
- }
-
- // Transaction tracking
- addTransaction(currency, amount, source) {
- const debugLogger = window.debugLogger;
- const transaction = {
- id: Date.now() + Math.random().toString(36).substr(2, 9),
- currency: currency,
- amount: amount,
- source: source,
- timestamp: Date.now()
- };
-
- this.transactionHistory.unshift(transaction);
-
- // Keep only last 100 transactions
- const oldLength = this.transactionHistory.length;
- if (this.transactionHistory.length > 100) {
- this.transactionHistory = this.transactionHistory.slice(0, 100);
- }
-
- if (debugLogger) debugLogger.logStep('Transaction recorded', {
- transactionId: transaction.id,
- currency: currency,
- amount: amount,
- source: source,
- timestamp: transaction.timestamp,
- historyLength: this.transactionHistory.length,
- wasTrimmed: oldLength > 100,
- removedCount: oldLength > 100 ? oldLength - 100 : 0
- });
- }
-
- // Shop functionality
- purchaseItem(itemId, quantity = 1) {
- const debugLogger = window.debugLogger;
- const item = this.findShopItem(itemId);
-
- if (!item) {
- if (debugLogger) debugLogger.logStep('Item purchase failed - item not found', {
- itemId: itemId,
- quantity: quantity
- });
- this.game.showNotification('Item not found in shop', 'error', 3000);
- return false;
- }
-
- const totalCost = item.price * quantity;
- const currency = item.currency;
- const oldCredits = this.credits;
- const oldGems = this.gems;
-
- if (debugLogger) debugLogger.logStep('Item purchase attempted', {
- itemId: itemId,
- itemName: item.name,
- itemType: item.type,
- quantity: quantity,
- unitPrice: item.price,
- totalCost: totalCost,
- currency: currency,
- currentCredits: oldCredits,
- currentGems: oldGems
- });
-
- // Check if player can afford
- if (currency === 'credits' && this.credits < totalCost) {
- if (debugLogger) debugLogger.logStep('Item purchase failed - insufficient credits', {
- totalCost: totalCost,
- currentCredits: oldCredits,
- deficit: totalCost - oldCredits
- });
- this.game.showNotification('Not enough credits!', 'error', 3000);
- return false;
- }
-
- if (currency === 'gems' && this.gems < totalCost) {
- if (debugLogger) debugLogger.logStep('Item purchase failed - insufficient gems', {
- totalCost: totalCost,
- currentGems: oldGems,
- deficit: totalCost - oldGems
- });
- this.game.showNotification('Not enough gems!', 'error', 3000);
- return false;
- }
-
- // Check if already owns this cosmetic
- if (item.type === 'cosmetic' && this.ownedCosmetics.includes(item.id)) {
- this.game.showNotification('You already own this cosmetic!', 'error', 3000);
- return false;
- }
-
- // Process payment and give item based on type
- if (currency === 'credits') {
- this.credits -= totalCost;
- } else if (currency === 'gems') {
- this.gems -= totalCost;
- }
-
- switch (item.type) {
- case 'ship':
- this.purchaseShip(item, quantity);
- break;
- case 'cosmetic':
- this.purchaseCosmetic(item, quantity);
- break;
- case 'consumable':
- this.purchaseConsumable(item, quantity);
- break;
- case 'material':
- this.purchaseMaterial(item, quantity);
- break;
- default:
- console.warn(`[ECONOMY] Unknown item type: ${item.type}`);
- return false;
- }
-
- // Random shop items don't have purchase limits - unlimited purchases allowed
- if (item.isRandomItem) {
- const category = this.getItemCategory(item);
-
- if (debugLogger) debugLogger.logStep('Random shop purchase completed', {
- itemId: itemId,
- itemName: item.name,
- category: category
- });
- }
-
- // Update inventory UI to show the new item
- if (this.game.systems.ui && this.game.systems.ui.updateInventory) {
- this.game.systems.ui.updateInventory();
- } else if (this.game.systems.ui && this.game.systems.ui.updateUI) {
- this.game.systems.ui.updateUI();
- } else {
- console.warn('No UI update method available after purchase');
- }
-
- if (debugLogger) debugLogger.logStep('Item purchase completed successfully', {
- itemId: itemId,
- itemName: item.name,
- itemType: item.type,
- quantity: quantity,
- totalCost: totalCost,
- currency: currency,
- oldCredits: oldCredits,
- newCredits: this.credits,
- oldGems: oldGems,
- newGems: this.gems
- });
-
- // Update UI without calling updateShopUI to avoid circular updates
- return true;
- }
-
- findShopItem(itemId) {
- const debugLogger = window.debugLogger;
-
- for (const category of Object.values(this.shopItems)) {
- const item = category.find(i => i.id === itemId);
- if (item) {
- if (debugLogger) debugLogger.logStep('Shop item found', {
- itemId: itemId,
- itemName: item.name,
- itemType: item.type,
- itemPrice: item.price,
- itemCurrency: item.currency
- });
- return item;
- }
- }
-
- if (debugLogger) debugLogger.logStep('Shop item not found', {
- itemId: itemId,
- availableCategories: Object.keys(this.shopItems)
- });
- return null;
- }
-
- purchaseShip(ship) {
- const debugLogger = window.debugLogger;
- const player = this.game.systems.player;
- const oldShipName = player.ship.name;
- const oldShipClass = player.ship.class;
- const oldAttributes = { ...player.attributes };
-
- if (debugLogger) debugLogger.logStep('Ship purchase processing', {
- shipId: ship.id,
- shipName: ship.name,
- shipType: ship.type,
- shipStats: ship.stats,
- oldShipName: oldShipName,
- oldShipClass: oldShipClass
- });
-
- // Add ship to player's ship collection
- // Add ship to base gallery
- if (this.game.systems.baseSystem) {
- this.game.systems.baseSystem.addShipToGallery(ship);
- if (debugLogger) debugLogger.logStep('Ship added to base gallery');
- }
-
- // Replace current ship (no upgrade functionality)
- player.ship.name = ship.name;
- player.ship.class = ship.type;
- player.ship.level = 1; // Reset level for new ship
- player.ship.texture = ship.texture; // Copy texture for image display
-
- // Set ship-specific stats (not just attributes)
- if (ship.stats.health) {
- player.ship.maxHealth = ship.stats.health;
- player.ship.health = ship.stats.health;
- }
- if (ship.stats.attack) {
- player.ship.attack = ship.stats.attack;
- }
- if (ship.stats.defense) {
- player.ship.defense = ship.stats.defense;
- }
- if (ship.stats.speed) {
- player.ship.speed = ship.stats.speed;
- }
- if (ship.stats.criticalChance) {
- player.ship.criticalChance = ship.stats.criticalChance;
- }
- if (ship.stats.criticalDamage) {
- player.ship.criticalDamage = ship.stats.criticalDamage;
- }
-
- // Also update player attributes for compatibility
- for (const [stat, value] of Object.entries(ship.stats)) {
- if (player.attributes[stat] !== undefined) {
- player.attributes[stat] = value; // Replace stats instead of adding
- }
- }
-
- player.updateUI();
-
- // Also update ShipSystem display
- if (this.game.systems.ship && this.game.systems.ship.updateCurrentShipDisplay) {
- this.game.systems.ship.updateCurrentShipDisplay();
- }
-
- if (debugLogger) debugLogger.logStep('Ship purchase completed', {
- shipId: ship.id,
- oldShipName: oldShipName,
- newShipName: player.ship.name,
- oldShipClass: oldShipClass,
- newShipClass: player.ship.class,
- attributeChanges: Object.entries(ship.stats).map(([stat, value]) => ({
- stat: stat,
- oldValue: oldAttributes[stat],
- newValue: player.attributes[stat],
- change: value
- }))
- });
- }
-
- purchaseCosmetic(cosmetic) {
- const debugLogger = window.debugLogger;
- const oldOwnedCount = this.ownedCosmetics.length;
-
- // Add to owned cosmetics
- this.ownedCosmetics.push(cosmetic.id);
- this.game.showNotification(`Cosmetic unlocked: ${cosmetic.name}`, 'success', 3000);
-
- if (debugLogger) debugLogger.logStep('Cosmetic purchase completed', {
- cosmeticId: cosmetic.id,
- cosmeticName: cosmetic.name,
- oldOwnedCount: oldOwnedCount,
- newOwnedCount: this.ownedCosmetics.length,
- totalOwnedCosmetics: this.ownedCosmetics.length
- });
- }
-
- purchaseConsumable(consumable, quantity) {
- const debugLogger = window.debugLogger;
- const inventory = this.game.systems.inventory;
- const oldInventorySize = inventory ? inventory.items.length : 0;
-
- if (debugLogger) debugLogger.logStep('Consumable purchase processing', {
- consumableId: consumable.id,
- consumableName: consumable.name,
- quantity: quantity,
- effect: consumable.effect,
- oldInventorySize: oldInventorySize
- });
-
- // Add to inventory
- for (let i = 0; i < quantity; i++) {
- const item = {
- id: `${consumable.id}_${Date.now()}_${i}`,
- name: consumable.name,
- type: 'consumable',
- rarity: consumable.rarity,
- quantity: 1,
- description: consumable.description,
- consumable: true,
- effect: consumable.effect
- };
-
- inventory.addItem(item);
- }
-
- if (debugLogger) debugLogger.logStep('Consumable purchase completed', {
- consumableId: consumable.id,
- quantity: quantity,
- oldInventorySize: oldInventorySize,
- newInventorySize: inventory ? inventory.items.length : 0,
- itemsAdded: quantity
- });
- }
-
- purchaseEquipment(equipment, quantity = 1) {
- const inventory = this.game.systems.inventory;
- const oldInventorySize = inventory ? inventory.items.length : 0;
-
- // Add to inventory
- for (let i = 0; i < quantity; i++) {
- const item = {
- id: `${equipment.id}_${Date.now()}_${i}`,
- name: equipment.name,
- type: equipment.type,
- rarity: equipment.rarity,
- quantity: 1,
- description: equipment.description,
- texture: equipment.texture,
- stats: equipment.stats || {},
- equipable: true
- };
-
- inventory.addItem(item);
- }
- }
-
- purchaseMaterial(material, quantity = 1) {
- const inventory = this.game.systems.inventory;
- const debugLogger = window.debugLogger;
-
- if (!inventory) {
- console.error('[ECONOMY] Inventory system not available for material purchase');
- return false;
- }
-
- try {
- const oldInventorySize = inventory.items.length;
- console.log(`[DEBUG] Inventory state before purchase: ${oldInventorySize} items`);
-
- // Create item object for inventory
- const item = {
- id: material.id,
- name: material.name,
- type: 'material',
- rarity: material.rarity || 'common',
- quantity: quantity,
- description: material.description || `A ${material.name} material`,
- stackable: true,
- stats: {},
- equipable: false,
- slot: null
- };
-
- console.log(`[DEBUG] Adding item to inventory: ${item.name}`);
- const added = inventory.addItem(item);
-
- console.log(`[DEBUG] addItem() returned: ${added}, new inventory size: ${inventory.items.length}`);
-
- if (debugLogger) debugLogger.logStep('Material purchase completed', {
- materialId: material.id,
- materialName: material.name,
- quantity: quantity,
- oldInventorySize: oldInventorySize,
- newInventorySize: inventory.items.length,
- addedSuccessfully: added
- });
-
- if (!added) {
- console.error('Failed to add material to inventory');
- return false;
- } else {
- // Update inventory UI to show the new material
- if (this.game.systems.ui && this.game.systems.ui.updateInventory) {
- this.game.systems.ui.updateInventory();
- } else if (this.game.systems.ui && this.game.systems.ui.updateUI) {
- this.game.systems.ui.updateUI();
- } else {
- console.warn('No UI update method available after material purchase');
- }
- console.log('[DEBUG] Material purchase completed and UI update called');
- return true;
- }
- } catch (error) {
- console.error('Exception in purchaseMaterial:', error);
- if (debugLogger) debugLogger.errorEvent('Purchase Material Exception', error, {
- materialId: material.id,
- materialName: material.name
- });
- return false;
- }
- }
-
- // Reward generation
- generateRewards(difficulty = 'normal', source = 'dungeon') {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) debugLogger.startStep('generateRewards', {
- difficulty: difficulty,
- source: source
- });
-
- const baseRewards = {
- easy: { credits: [50, 150], gems: [0, 1], experience: [20, 50] },
- normal: { credits: [100, 300], gems: [1, 3], experience: [50, 100] },
- hard: { credits: [200, 500], gems: [2, 5], experience: [100, 200] },
- extreme: { credits: [500, 1000], gems: [5, 10], experience: [200, 400] }
- };
-
- const rewards = baseRewards[difficulty] || baseRewards.normal;
- const generatedRewards = {};
-
- // Generate credits
- generatedRewards.credits = Math.floor(
- Math.random() * (rewards.credits[1] - rewards.credits[0] + 1) + rewards.credits[0]
- );
-
- // Generate gems
- generatedRewards.gems = Math.floor(
- Math.random() * (rewards.gems[1] - rewards.gems[0] + 1) + rewards.gems[0]
- );
-
- // Generate experience
- generatedRewards.experience = Math.floor(
- Math.random() * (rewards.experience[1] - rewards.experience[0] + 1) + rewards.experience[0]
- );
-
- // Bonus for premium currency (rare)
- if (Math.random() < 0.05) { // 5% chance
- generatedRewards.premiumCurrency = 1;
- }
-
- if (debugLogger) debugLogger.endStep('generateRewards', {
- difficulty: difficulty,
- source: source,
- generatedRewards: generatedRewards,
- rewardRanges: rewards
- });
-
- return generatedRewards;
- }
-
- giveRewards(rewards, source = 'unknown') {
- const debugLogger = window.debugLogger;
- const player = this.game.systems.player;
- const oldCredits = this.credits;
- const oldGems = this.gems;
- const oldExperience = player.stats.experience;
- const oldLevel = player.stats.level;
-
- if (debugLogger) debugLogger.startStep('giveRewards', {
- source: source,
- rewards: rewards,
- oldPlayerState: {
- credits: oldCredits,
- gems: oldGems,
- experience: oldExperience,
- level: oldLevel
- }
- });
-
- let totalRewards = [];
-
- // Add credits
- if (rewards.credits > 0) {
- this.addCredits(rewards.credits, source);
- totalRewards.push(`${rewards.credits} credits`);
- }
-
- // Add gems
- if (rewards.gems > 0) {
- this.addGems(rewards.gems, source);
- totalRewards.push(`${rewards.gems} gems`);
- }
-
- // Add premium currency
- if (rewards.premiumCurrency > 0) {
- this.addPremiumCurrency(rewards.premiumCurrency, source);
- totalRewards.push(`${rewards.premiumCurrency} premium currency`);
- }
-
- // Add experience
- if (rewards.experience > 0) {
- player.addExperience(rewards.experience);
- totalRewards.push(`${rewards.experience} experience`);
- }
-
- // Add materials
- if (rewards.materials && rewards.materials.length > 0) {
- const inventory = this.game.systems.inventory;
- alert(`[DUNGEON DEBUG] Adding ${rewards.materials.length} materials to inventory\nInventory available: ${!!inventory}\nMaterials: ${JSON.stringify(rewards.materials, null, 2)}`);
-
- for (const material of rewards.materials) {
- // Create proper item object like in purchaseMaterial
- const itemObject = {
- id: material.id,
- name: material.id.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()), // Convert ID to readable name
- type: 'material',
- rarity: 'common',
- quantity: material.quantity,
- description: `A ${material.id.replace('_', ' ')} material`,
- stackable: true,
- stats: {},
- equipable: false,
- slot: null
- };
-
- alert(`[DUNGEON DEBUG] Fixed! Adding material as full item object:\n${JSON.stringify(itemObject, null, 2)}`);
- inventory.addItem(itemObject);
- totalRewards.push(`${material.quantity}x ${material.id}`);
- }
- }
-
- // Add items
- if (rewards.items && rewards.items.length > 0) {
- const inventory = this.game.systems.inventory;
- for (const item of rewards.items) {
- inventory.addItem(item.id, item.quantity || 1);
- totalRewards.push(`${item.quantity || 1}x ${item.id}`);
- }
- }
-
- // Show reward notification
- if (totalRewards.length > 0) {
- const rewardText = totalRewards.join(', ');
- this.game.showNotification(`Rewards: ${rewardText}`, 'success', 3000);
- }
-
- if (debugLogger) debugLogger.endStep('giveRewards', {
- source: source,
- rewardsGiven: rewards,
- totalRewardsText: totalRewards.join(', '),
- currencyChanges: {
- credits: { old: oldCredits, new: this.credits, change: this.credits - oldCredits },
- gems: { old: oldGems, new: this.gems, change: this.gems - oldGems }
- },
- playerChanges: {
- experience: { old: oldExperience, new: player.stats.experience, change: player.stats.experience - oldExperience },
- level: { old: oldLevel, new: player.stats.level, leveledUp: player.stats.level > oldLevel }
- }
- });
- }
-
- // Daily rewards and bonuses
- claimDailyReward() {
- const debugLogger = window.debugLogger;
- const lastClaim = localStorage.getItem('lastDailyReward');
- const today = new Date().toDateString();
-
- if (debugLogger) debugLogger.startStep('claimDailyReward', {
- lastClaim: lastClaim,
- today: today,
- alreadyClaimed: lastClaim === today
- });
-
- if (lastClaim === today) {
- this.game.showNotification('Daily reward already claimed!', 'warning', 3000);
- if (debugLogger) debugLogger.endStep('claimDailyReward', {
- success: false,
- reason: 'Already claimed today'
- });
- return false;
- }
-
- // Calculate reward based on consecutive days
- const consecutiveDays = parseInt(localStorage.getItem('consecutiveDailyRewards') || '0') + 1;
- const baseReward = 100;
- const bonusMultiplier = Math.min(consecutiveDays * 0.1, 2); // Max 2x bonus
- const credits = Math.floor(baseReward * (1 + bonusMultiplier));
- const gems = Math.min(Math.floor(consecutiveDays / 7), 5); // 1 gem per week, max 5
-
- if (debugLogger) debugLogger.logStep('Daily reward calculation', {
- consecutiveDays: consecutiveDays,
- baseReward: baseReward,
- bonusMultiplier: bonusMultiplier,
- creditsReward: credits,
- gemsReward: gems
- });
-
- // Give rewards
- this.addCredits(credits, 'daily_reward');
- this.addGems(gems, 'daily_reward');
-
- // Update daily reward tracking
- localStorage.setItem('lastDailyReward', today);
- localStorage.setItem('consecutiveDailyRewards', consecutiveDays.toString());
-
- // Show notification
- const rewardText = `${credits} credits${gems > 0 ? ` and ${gems} gems` : ''}`;
- this.game.showNotification(`Daily reward claimed: ${rewardText}! (${consecutiveDays} day streak)`, 'success', 4000);
-
- if (debugLogger) debugLogger.endStep('claimDailyReward', {
- success: true,
- consecutiveDays: consecutiveDays,
- creditsReward: credits,
- gemsReward: gems,
- rewardText: rewardText,
- newLastClaim: today,
- newConsecutiveDays: consecutiveDays
- });
-
- return true;
- }
-
- // UI updates
- updateUI() {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) debugLogger.startStep('updateUI', {
- currentCredits: this.credits,
- currentGems: this.gems,
- currentPremiumCurrency: this.premiumCurrency
- });
-
- // Update resource displays
- const creditsElement = document.getElementById('credits');
- if (creditsElement) {
- creditsElement.textContent = this.game.formatNumber(this.credits);
- }
-
- const gemsElement = document.getElementById('gems');
- if (gemsElement) {
- gemsElement.textContent = this.game.formatNumber(this.gems);
- }
-
- const premiumElement = document.getElementById('premiumCurrency');
- if (premiumElement) {
- premiumElement.textContent = this.game.formatNumber(this.premiumCurrency);
- }
-
- // Update shop UI when shop tab is active
- this.updateShopUI();
-
- if (debugLogger) debugLogger.endStep('updateUI', {
- creditsUpdated: !!creditsElement,
- gemsUpdated: !!gemsElement,
- premiumUpdated: !!premiumElement,
- shopUIUpdated: true
- });
- }
-
- // Reset economy (for new game)
- reset() {
- const debugLogger = window.debugLogger;
- const oldState = {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactionCount: this.transactions.length,
- ownedCosmeticsCount: this.ownedCosmetics.length
- };
-
- if (debugLogger) debugLogger.startStep('reset', {
- oldState: oldState
- });
-
- this.credits = 1000;
- this.gems = 10;
- this.premiumCurrency = 0;
- this.transactions = [];
- this.ownedCosmetics = [];
-
- // Reset daily rewards
- localStorage.removeItem('lastDailyReward');
- localStorage.removeItem('consecutiveDailyRewards');
-
- if (debugLogger) debugLogger.endStep('reset', {
- oldState: oldState,
- newState: {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactionCount: this.transactions.length,
- ownedCosmeticsCount: this.ownedCosmetics.length
- }
- });
- }
-
- // Clear all data
- clear() {
- const debugLogger = window.debugLogger;
- const oldState = {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactionCount: this.transactions.length,
- ownedCosmeticsCount: this.ownedCosmetics.length
- };
-
- if (debugLogger) debugLogger.startStep('clear', {
- oldState: oldState
- });
-
- this.credits = 0;
- this.gems = 0;
- this.premiumCurrency = 0;
- this.transactions = [];
- this.ownedCosmetics = [];
-
- if (debugLogger) debugLogger.endStep('clear', {
- oldState: oldState,
- newState: {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactionCount: this.transactions.length,
- ownedCosmeticsCount: this.ownedCosmetics.length
- }
- });
- }
-
- updateShopUI() {
- const debugLogger = window.debugLogger;
- const shopItemsElement = document.getElementById('shopItems');
-
- if (!shopItemsElement) {
- if (debugLogger) debugLogger.log('updateShopUI: Shop items element not found');
- return;
- }
-
- const activeCategory = document.querySelector('.shop-cat-btn.active')?.dataset.category || 'ships';
-
- // Always use random shop items when the system is available
- // If no random items exist for this category, trigger a refresh
- if (!this.randomShopItems[activeCategory] || this.randomShopItems[activeCategory].length === 0) {
- console.log(`[ECONOMY] No random items for ${activeCategory}, triggering refresh`);
- this.refreshRandomShop();
- }
-
- const items = this.randomShopItems[activeCategory] || [];
- const isRandomShop = true; // Always random shop when system is available
-
- if (debugLogger) debugLogger.startStep('updateShopUI', {
- activeCategory: activeCategory,
- itemCount: items.length,
- isRandomShop: isRandomShop,
- currentCredits: this.credits,
- currentGems: this.gems,
- currentPremiumCurrency: this.premiumCurrency
- });
-
- shopItemsElement.innerHTML = '';
-
- // Add shop refresh info if using random shop
- if (isRandomShop) {
- const refreshInfo = document.createElement('div');
- refreshInfo.className = 'shop-refresh-info';
- refreshInfo.innerHTML = `
-
-
-
- Next refresh in: ${this.getShopRefreshCountdown()}
-
-
- `;
- shopItemsElement.appendChild(refreshInfo);
- }
-
- items.forEach(item => {
- const itemElement = document.createElement('div');
- itemElement.className = 'shop-item';
-
- const canAfford = this.canAfford(item);
- const isOwned = item.type === 'cosmetic' && this.ownedCosmetics.includes(item.id);
-
- itemElement.innerHTML = `
-
- ${item.texture ?
- `
-
-
` : ''}
-
-
${item.name}
-
${item.description}
- ${item.stats ? `
-
- ${Object.entries(item.stats).map(([stat, value]) =>
- `
${stat}: ${value}
`
- ).join('')}
-
- ` : ''}
-
${this.formatPrice(item)}
-
${item.rarity}
-
-
-
- ${isOwned ? 'Owned' : canAfford ? 'Purchase' : 'Cannot Afford'}
-
- `;
-
- shopItemsElement.appendChild(itemElement);
-
- if (debugLogger) debugLogger.logStep('Shop item rendered', {
- itemId: item.id,
- itemName: item.name,
- itemCategory: activeCategory,
- itemPrice: item.price,
- itemCurrency: item.currency,
- canAfford: canAfford,
- isOwned: isOwned
- });
- });
-
- // Re-setup event listeners since we just recreated all the buttons
- this.setupShopEventListeners();
-
- if (debugLogger) debugLogger.endStep('updateShopUI', {
- activeCategory: activeCategory,
- itemsRendered: items.length,
- shopUIUpdated: true
- });
- }
-
- // Testing and utility methods
- testPurchase(itemId) {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) debugLogger.startStep('testPurchase', {
- itemId: itemId
- });
-
- const item = this.findShopItem(itemId);
- if (!item) {
- if (debugLogger) debugLogger.endStep('testPurchase', {
- success: false,
- reason: 'Item not found',
- itemId: itemId
- });
- return false;
- }
-
- const result = this.purchaseItem(itemId);
-
- if (debugLogger) debugLogger.endStep('testPurchase', {
- success: result,
- itemId: itemId,
- itemName: item.name,
- itemCategory: item.type
- });
-
- return result;
- }
-
- formatItemStats(item) {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) debugLogger.startStep('formatItemStats', {
- itemId: item.id,
- itemType: item.type
- });
-
- let stats = [];
-
- if (item.stats) {
- for (const [stat, value] of Object.entries(item.stats)) {
- stats.push(`${stat}: +${value}`);
- }
- }
-
- if (item.effect) {
- if (item.effect.attackMultiplier) {
- stats.push(`Attack: x${item.effect.attackMultiplier}`);
- }
- if (item.effect.defense) {
- stats.push(`Defense: +${item.effect.defense}`);
- }
- if (item.effect.healing) {
- stats.push(`Healing: +${item.effect.healing}`);
- }
- if (item.effect.energyRestore) {
- stats.push(`Energy: +${item.effect.energyRestore}`);
- }
- }
-
- const formattedStats = stats.length > 0 ? stats.join(', ') : 'No special stats';
-
- if (debugLogger) debugLogger.endStep('formatItemStats', {
- itemId: item.id,
- formattedStats: formattedStats,
- statCount: stats.length
- });
-
- return formattedStats;
- }
-
- // Save and load
- save() {
- const debugLogger = window.debugLogger;
-
- // if (debugLogger) debugLogger.startStep('save', {
- // currentCredits: this.credits,
- // currentGems: this.gems,
- // currentPremiumCurrency: this.premiumCurrency,
- // transactionCount: this.transactions.length,
- // ownedCosmeticsCount: this.ownedCosmetics.length
- // });
-
- const saveData = {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactions: this.transactions,
- ownedCosmetics: this.ownedCosmetics,
- // Include shop data with timestamp for proper refresh calculation
- shopData: {
- randomShopItems: this.randomShopItems,
- categoryPurchaseLimits: this.categoryPurchaseLimits,
- lastShopRefresh: this.lastShopRefresh,
- saveTimestamp: Date.now() // When this save was created
- }
- };
-
- // if (debugLogger) debugLogger.endStep('save', {
- // saveDataSize: JSON.stringify(saveData).length,
- // economyState: saveData
- // });
-
- return saveData;
- }
-
- load(data) {
- const debugLogger = window.debugLogger;
- const oldState = {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactionCount: this.transactions.length,
- ownedCosmeticsCount: this.ownedCosmetics.length
- };
-
- // if (debugLogger) debugLogger.startStep('load', {
- // oldState: oldState,
- // loadData: data
- // });
-
- try {
- this.credits = data.credits || 0;
- this.gems = data.gems || 0;
- this.premiumCurrency = data.premiumCurrency || 0;
- this.transactions = data.transactions || [];
- this.ownedCosmetics = data.ownedCosmetics || [];
-
- // Load shop data and calculate if refresh is needed
- if (data.shopData) {
- console.log('[ECONOMY] Loading shop data from save');
-
- // Restore shop data
- this.randomShopItems = data.shopData.randomShopItems || {};
- this.categoryPurchaseLimits = data.shopData.categoryPurchaseLimits || {};
- this.lastShopRefresh = data.shopData.lastShopRefresh || null;
-
- // Calculate if shop should have refreshed while game was closed
- const saveTimestamp = data.shopData.saveTimestamp || Date.now();
- const currentTime = Date.now();
- const timeSinceSave = currentTime - saveTimestamp;
-
- if (this.lastShopRefresh) {
- const timeSinceLastRefresh = currentTime - this.lastShopRefresh;
- const totalIntervalsPassed = Math.floor(timeSinceLastRefresh / this.SHOP_REFRESH_INTERVAL);
-
- if (totalIntervalsPassed > 0) {
- console.log(`[ECONOMY] Shop missed ${totalIntervalsPassed} refresh(es) while game was closed, refreshing now`);
- this.refreshRandomShop();
- } else {
- console.log('[ECONOMY] Shop items still valid, no refresh needed');
- }
- } else {
- console.log('[ECONOMY] No previous shop refresh, generating new shop');
- this.refreshRandomShop();
- }
- } else {
- console.log('[ECONOMY] No shop data in save, initializing new shop');
- this.refreshRandomShop();
- }
-
- if (debugLogger) debugLogger.endStep('load', {
- success: true,
- oldState: oldState,
- newState: {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactionCount: this.transactions.length,
- ownedCosmeticsCount: this.ownedCosmetics.length
- }
- });
- } catch (error) {
- if (debugLogger) debugLogger.errorEvent('load', error, {
- oldState: oldState,
- error: error.message
- });
- throw error;
- }
- }
-
- // Utility methods for shop
- canAfford(item) {
- if (item.currency === 'credits') {
- return this.credits >= item.price;
- } else if (item.currency === 'gems') {
- return this.gems >= item.price;
- } else if (item.currency === 'premium') {
- return this.premiumCurrency >= item.price;
- }
- return false;
- }
-
- formatPrice(item) {
- const currencySymbols = {
- 'credits': '₵',
- 'gems': '💎',
- 'premium': '⭐'
- };
-
- const symbol = currencySymbols[item.currency] || item.currency;
- return `${symbol}${this.game.formatNumber(item.price)}`;
- }
-}
diff --git a/Client-Server/js/core/GameEngine.js b/Client-Server/js/core/GameEngine.js
deleted file mode 100644
index f991638..0000000
--- a/Client-Server/js/core/GameEngine.js
+++ /dev/null
@@ -1,1567 +0,0 @@
-/**
- * Galaxy Strike Online - Game Engine
- * Core game loop and state management
- */
-
-class GameEngine extends EventTarget {
- constructor() {
- super(); // Call EventTarget constructor first
-
- console.log('🛑 DEBUG STOP 1: GameEngine constructor starting');
- const debugLogger = window.debugLogger;
- if (debugLogger) debugLogger.log('GameEngine constructor called', {
- autoSaveInterval: 5,
- timestamp: new Date().toISOString()
- });
-
- this.isRunning = false;
- this.lastUpdate = 0;
- this.gameTime = 0;
-
- // Auto-save settings
- this.autoSaveInterval = 1; // Default 1 minute
- this.autoSaveTimer = null;
- this.lastAutoSave = 0;
-
- // Save slot information
- this.saveSlotInfo = {
- slot: 1, // Default save slot
- useFileSystem: !!window.electronAPI // Use file system if Electron API is available
- };
-
- console.log('🛑 DEBUG STOP 2: Save slot info initialized:', this.saveSlotInfo);
-
- // GUI update settings
- this.guiUpdateInterval = 1000; // Update GUI once per second (1000ms)
- this.lastGUIUpdate = 0;
-
- // Game logic settings (independent of frame rate)
- this.gameLogicInterval = 1000; // Update game logic every 1 second
- this.gameLogicTimer = null;
-
- // Game state
- this.state = {
- paused: false,
- currentTab: 'dashboard',
- notifications: [],
- loading: true
- };
-
- console.log('🛑 DEBUG STOP 3: Game state initialized:', this.state);
-
- // Systems
- this.systems = {};
-
- // Event listeners
- this.eventListeners = new Map();
-
- console.log('🛑 DEBUG STOP 4: About to call this.init()');
- this.init();
- console.log('🛑 DEBUG STOP 5: GameEngine constructor completed');
- }
-
- setMultiplayerMode(isMultiplayer, socket = null, serverData = null, currentUser = null) {
- const debugLogger = window.debugLogger;
-
- console.log('[GAME ENGINE] Setting multiplayer mode:', isMultiplayer);
- if (debugLogger) debugLogger.logStep('setMultiplayerMode', { isMultiplayer });
-
- this.isMultiplayer = isMultiplayer;
- this.socket = socket;
- this.serverData = serverData;
- this.currentUser = currentUser;
-
- // Store multiplayer settings for systems that need them
- this.multiplayerConfig = {
- isMultiplayer,
- socket,
- serverData,
- currentUser
- };
-
- console.log('[GAME ENGINE] Multiplayer mode configured:', {
- isMultiplayer,
- hasSocket: !!socket,
- hasServerData: !!serverData,
- hasCurrentUser: !!currentUser
- });
- }
-
- // Get random integer between min and max (inclusive)
- getRandomInt(min, max) {
- return Math.floor(Math.random() * (max - min + 1)) + min;
- }
-
- // Get random float between min and max
- getRandomFloat(min, max) {
- return Math.random() * (max - min) + min;
- }
-
- async init() {
- console.log('[GAME ENGINE] Initializing game engine');
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- if (logger) await logger.info('Initializing game engine');
- if (debugLogger) await debugLogger.startStep('gameEngineInit');
-
- try {
- // Initialize core systems but don't setup new game data
- await this.initializeSystemsForLoad();
-
- if (debugLogger) await debugLogger.logStep('Systems initialized, setting up event listeners');
-
- // Set up event listeners
- await this.setupEventListeners();
-
- if (debugLogger) await debugLogger.logStep('Event listeners setup complete');
-
- if (logger) await logger.info('Game engine initialization completed');
- if (debugLogger) await debugLogger.endStep('gameEngineInit', {
- systemsInitialized: Object.keys(this.systems).length,
- eventListeners: this.eventListeners.size
- });
-
- } catch (error) {
- console.error('Failed to initialize game:', error);
- if (logger) await logger.errorEvent(error, 'Game Engine Initialization');
- if (debugLogger) await debugLogger.errorEvent(error, 'Game Engine Initialization');
- }
- }
-
- async startGame(continueGame = false) {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- console.log('[GAME ENGINE] startGame called with continueGame =', continueGame);
- if (logger) await logger.info('Starting game', { continueGame });
- if (debugLogger) await debugLogger.startStep('startGame', { continueGame });
-
- try {
- if (continueGame) {
- console.log('[GAME ENGINE] Loading existing save data...');
- if (debugLogger) await debugLogger.logStep('Loading existing save data');
- await this.loadGame();
- console.log('[GAME ENGINE] Save data loaded');
- } else {
- console.log('[GAME ENGINE] Creating new game...');
- if (debugLogger) await debugLogger.logStep('Creating new game');
- await this.newGame();
- console.log('[GAME ENGINE] New game created');
- }
-
- // Start game loop
- // CRITICAL: Ensure UIManager is initialized before starting game
- if (this.systems.ui) {
- console.log('[GAME ENGINE] Final UIManager initialization check...');
- try {
- await this.systems.ui.initialize();
- console.log('[GAME ENGINE] UIManager initialized successfully');
- } catch (error) {
- console.error('[GAME ENGINE] UIManager initialization failed:', error);
- }
- }
-
- this.start();
- console.log('[GAME ENGINE] Game loop started');
- if (debugLogger) await debugLogger.logStep('Game loop started');
-
- const loadingStatus = document.getElementById('loadingStatus');
- if (loadingStatus) {
- console.log('[GAME ENGINE] Hiding loading status text');
- if (debugLogger) await debugLogger.logStep('Hiding loading status text');
- loadingStatus.classList.add('hidden');
- }
-
- const gameInterface = document.getElementById('gameInterface');
- if (gameInterface) {
- console.log('[GAME ENGINE] Showing game interface');
- if (debugLogger) await debugLogger.logStep('Showing game interface');
- gameInterface.classList.remove('hidden');
-
- // CRITICAL: Initialize UIManager after showing interface
- console.log('[GAME ENGINE] Initializing UIManager after showing interface...');
- if (this.systems.ui) {
- try {
- await this.systems.ui.initialize();
- console.log('[GAME ENGINE] UIManager initialized successfully (post-interface)');
- } catch (error) {
- console.error('[GAME ENGINE] UIManager initialization failed:', error);
- }
- } else {
- console.log('[GAME ENGINE] UIManager not found when trying to initialize after interface');
- }
- } else {
- console.warn('[GAME ENGINE] gameInterface element not found');
- if (debugLogger) await debugLogger.warn('gameInterface element not found');
- }
-
- if (logger) await logger.info('Game started successfully');
- if (debugLogger) await debugLogger.endStep('startGame', {
- continueGame,
- isRunning: this.isRunning,
- gameTime: this.gameTime
- });
-
- } catch (error) {
- console.error('[GAME ENGINE] Failed to start game:', error);
- if (logger) await logger.errorEvent(error, 'Game Start');
- if (debugLogger) await debugLogger.errorEvent(error, 'Game Start');
- }
- }
-
- async initializeSystemsForLoad() {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- if (debugLogger) await debugLogger.startStep('initializeSystemsForLoad');
-
- if (logger) {
- await logger.timeAsync('Game Systems Initialization for Load', async () => {
- await logger.info('Initializing game systems for loading');
-
- if (debugLogger) await debugLogger.logStep('Initializing TextureManager');
- // Initialize texture manager first
- this.systems.textureManager = new TextureManager(this);
- if (logger) await logger.systemEvent('TextureManager', 'Initialized');
- if (debugLogger) await debugLogger.logStep('TextureManager initialized');
-
- if (debugLogger) await debugLogger.logStep('Creating Player system (without initialization)');
- // Create systems but don't initialize with default data
- this.systems.player = new Player(this);
- if (logger) await logger.systemEvent('Player', 'Created');
- if (debugLogger) await debugLogger.logStep('Player system created');
-
- if (debugLogger) await debugLogger.logStep('Creating Inventory system (without initialization)');
- this.systems.inventory = new Inventory(this);
- if (logger) await logger.systemEvent('Inventory', 'Created');
- if (debugLogger) await debugLogger.logStep('Inventory system created');
-
- if (debugLogger) await debugLogger.logStep('Creating Economy system (without initialization)');
- this.systems.economy = new Economy(this);
- if (logger) await logger.systemEvent('Economy', 'Created');
- if (debugLogger) await debugLogger.logStep('Economy system created');
-
- if (debugLogger) await debugLogger.logStep('Creating IdleSystem');
- this.systems.idleSystem = new IdleSystem(this);
- if (logger) await logger.systemEvent('IdleSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('IdleSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating DungeonSystem');
- this.systems.dungeonSystem = new DungeonSystem(this);
- if (logger) await logger.systemEvent('DungeonSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('DungeonSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating SkillSystem');
- this.systems.skillSystem = new SkillSystem(this);
- if (logger) await logger.systemEvent('SkillSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('SkillSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating BaseSystem');
- this.systems.baseSystem = new BaseSystem(this);
- if (logger) await logger.systemEvent('BaseSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('BaseSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating QuestSystem');
- this.systems.questSystem = new QuestSystem(this);
- if (logger) await logger.systemEvent('QuestSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('QuestSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating ShipSystem');
- this.systems.shipSystem = new ShipSystem(this);
- if (logger) await logger.systemEvent('ShipSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('ShipSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating UIManager');
- if (typeof UIManager !== 'undefined') {
- console.log('[GAME ENGINE] UIManager class found, creating real UIManager');
- this.systems.ui = new UIManager(this);
- // Expose UIManager globally for button onclick handlers
- window.uiManager = this.systems.ui;
- window.game.systems.ui = this.systems.ui;
- if (logger) await logger.systemEvent('UIManager', 'Created');
- if (debugLogger) await debugLogger.logStep('UIManager created and exposed');
- } else {
- console.error('[GAME ENGINE] UIManager class not found - this should not happen!');
- if (debugLogger) await debugLogger.error('UIManager class not found - this should not happen!');
- // Create minimal UI system to prevent crashes (should never be needed based on logs)
- this.systems.ui = {
- showNotification: (message, type, duration) => {
- console.log(`[UI MINIMAL] ${type}: ${message}`);
- },
- updateUI: () => {
- console.log('[UI MINIMAL] updateUI called');
- },
- switchTab: (tabName) => {
- console.log('[UI MINIMAL] switchTab called');
- }
- };
- }
- if (debugLogger) await debugLogger.logStep('Creating CraftingSystem');
- this.systems.crafting = new CraftingSystem(this);
- if (logger) await logger.systemEvent('CraftingSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('CraftingSystem created');
-
- if (debugLogger) await debugLogger.endStep('initializeSystemsForLoad', {
- systemsCreated: Object.keys(this.systems).length
- });
- });
- }
- }
-
- async initializeSystems() {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- if (debugLogger) await debugLogger.startStep('initializeSystems');
-
- if (logger) {
- await logger.timeAsync('Game Systems Initialization', async () => {
- await logger.info('Initializing game systems');
-
- if (debugLogger) await debugLogger.logStep('Initializing TextureManager');
- // Initialize texture manager first
- this.systems.textureManager = new TextureManager(this);
- if (logger) await logger.systemEvent('TextureManager', 'Initialized');
- if (debugLogger) await debugLogger.logStep('TextureManager initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing Player system');
- // Initialize in dependency order
- this.systems.player = new Player(this);
- if (logger) await logger.systemEvent('Player', 'Initialized');
- if (debugLogger) await debugLogger.logStep('Player system initialized', {
- playerLevel: this.systems.player.stats.level,
- playerExperience: this.systems.player.stats.experience
- });
-
- if (debugLogger) await debugLogger.logStep('Initializing Inventory system');
- this.systems.inventory = new Inventory(this);
- if (logger) await logger.systemEvent('Inventory', 'Initialized');
- if (debugLogger) await debugLogger.logStep('Inventory system initialized', {
- inventorySize: this.systems.inventory.items.length
- });
-
- if (debugLogger) await debugLogger.logStep('Initializing Economy system');
- this.systems.economy = new Economy(this);
- if (logger) await logger.systemEvent('Economy', 'Initialized');
- if (debugLogger) await debugLogger.logStep('Economy system initialized', {
- credits: this.systems.economy.credits,
- gems: this.systems.economy.gems
- });
-
- if (debugLogger) await debugLogger.logStep('Initializing IdleSystem');
- this.systems.idleSystem = new IdleSystem(this);
- if (logger) await logger.systemEvent('IdleSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('IdleSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing DungeonSystem');
- this.systems.dungeonSystem = new DungeonSystem(this);
- if (logger) await logger.systemEvent('DungeonSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('DungeonSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing SkillSystem');
- this.systems.skillSystem = new SkillSystem(this);
- if (logger) await logger.systemEvent('SkillSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('SkillSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing BaseSystem');
- this.systems.baseSystem = new BaseSystem(this);
- if (logger) await logger.systemEvent('BaseSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('BaseSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing QuestSystem');
- this.systems.questSystem = new QuestSystem(this);
- await this.systems.questSystem.initialize();
- if (logger) await logger.systemEvent('QuestSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('QuestSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing ShipSystem');
- this.systems.ship = new ShipSystem(this);
- if (logger) await logger.systemEvent('ShipSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('ShipSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('UIManager already initialized in initializeSystemsForLoad');
-
- if (debugLogger) await debugLogger.logStep('Initializing CraftingSystem');
- this.systems.crafting = new CraftingSystem(this);
- if (logger) await logger.systemEvent('CraftingSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('CraftingSystem initialized');
-
- if (logger) await logger.info('All game systems initialized');
- if (debugLogger) await debugLogger.logStep('All systems created, running individual initializations');
-
- if (debugLogger) await debugLogger.logStep('Running individual system initializations');
- for (const [name, system] of Object.entries(this.systems)) {
- if (system.initialize) {
- if (debugLogger) await debugLogger.logStep(`Initializing ${name} system`);
- await system.initialize();
- if (debugLogger) await debugLogger.logStep(`${name} system initialization complete`);
- } else {
- if (debugLogger) await debugLogger.logStep(`${name} system has no initialize method`);
- }
- }
-
- if (debugLogger) await debugLogger.endStep('initializeSystems', {
- totalSystems: Object.keys(this.systems).length,
- systemsList: Object.keys(this.systems)
- });
- });
- } else {
- // Fallback without timing
- if (debugLogger) await debugLogger.logStep('Logger not available, using fallback initialization');
-
- // Initialize texture manager first
- this.systems.textureManager = new TextureManager(this);
-
- // Initialize in dependency order
- this.systems.player = new Player(this);
- this.systems.inventory = new Inventory(this);
- this.systems.economy = new Economy(this);
- this.systems.idleSystem = new IdleSystem(this);
- this.systems.dungeonSystem = new DungeonSystem(this);
- this.systems.skillSystem = new SkillSystem(this);
- this.systems.baseSystem = new BaseSystem(this);
- this.systems.questSystem = new QuestSystem(this);
- // UIManager already initialized in initializeSystemsForLoad
- this.systems.crafting = new CraftingSystem(this);
-
- for (const [name, system] of Object.entries(this.systems)) {
- if (system.initialize) {
- await system.initialize();
- }
- }
-
- if (debugLogger) await debugLogger.endStep('initializeSystems', {
- fallbackMode: true,
- totalSystems: Object.keys(this.systems).length
- });
- }
- }
-
- async setupEventListeners() {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) await debugLogger.startStep('setupEventListeners');
-
- // Window events
- if (debugLogger) await debugLogger.logStep('Setting up window event listeners');
- window.addEventListener('beforeunload', () => {
- if (debugLogger) debugLogger.logStep('beforeunload event triggered - saving game');
- this.save();
- });
-
- window.addEventListener('visibilitychange', () => {
- // if (debugLogger) debugLogger.logStep('visibilitychange event triggered', {
- // hidden: document.hidden,
- // visibilityState: document.visibilityState
- // });
- if (document.hidden) {
- // if (debugLogger) debugLogger.logStep('Document hidden - saving game');
- this.save();
- }
- });
-
- // Keyboard shortcuts
- if (debugLogger) await debugLogger.logStep('Setting up keyboard shortcuts');
- document.addEventListener('keydown', (event) => {
- if (debugLogger) debugLogger.logStep('Key pressed', {
- key: event.key,
- code: event.code,
- ctrlKey: event.ctrlKey,
- altKey: event.altKey,
- shiftKey: event.shiftKey
- });
-
- // Ctrl+S for manual save
- if (event.ctrlKey && event.key === 's') {
- event.preventDefault();
- if (debugLogger) debugLogger.logStep('Manual save shortcut triggered (Ctrl+S)');
- this.save();
- }
- });
-
- // Game loop events
- if (debugLogger) await debugLogger.logStep('Setting up game loop events');
- this.addEventListener('gameStarted', () => {
- if (debugLogger) debugLogger.logStep('Game started event received');
- });
-
- this.addEventListener('gameStopped', () => {
- if (debugLogger) debugLogger.logStep('Game stopped event received');
- });
-
- this.addEventListener('gameSaved', () => {
- // if (debugLogger) debugLogger.logStep('Game saved event received');
- });
-
- this.addEventListener('gameLoaded', () => {
- if (debugLogger) debugLogger.logStep('Game loaded event received');
- });
-
- // Setup UI event listeners
- if (debugLogger) await debugLogger.logStep('Setting up UI event listeners');
- if (this.systems.ui && typeof this.systems.ui.setupEventListeners === 'function' && !this.uiEventListenersSetup) {
- this.systems.ui.setupEventListeners();
- this.uiEventListenersSetup = true; // Prevent duplicate setup
- if (debugLogger) await debugLogger.logStep('UI event listeners setup complete');
- } else if (this.uiEventListenersSetup) {
- if (debugLogger) await debugLogger.logStep('UI event listeners already setup, skipping');
- }
-
- if (debugLogger) await debugLogger.endStep('setupEventListeners', {
- windowEvents: 2,
- keyboardShortcuts: 2,
- gameLoopEvents: 4
- });
- }
-
- start() {
- const debugLogger = window.debugLogger;
-
- if (this.isRunning) {
- if (debugLogger) debugLogger.log('GameEngine.start() called but game is already running', {
- isRunning: this.isRunning,
- gameTime: this.gameTime
- });
- return;
- }
-
- if (debugLogger) debugLogger.logStep('Starting game engine', {
- gameLogicInterval: this.gameLogicInterval
- });
-
- this.isRunning = true;
- this.lastUpdate = performance.now();
-
- if (debugLogger) debugLogger.logStep('Game engine started, beginning game loop');
-
- this.gameLoop();
-
- // Loading screen is now handled by startGame() method
- // Don't duplicate the hiding logic here
-
- if (debugLogger) debugLogger.logStep('Game loop initiated');
- }
-
- async stop() {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) await debugLogger.startStep('stopGame');
-
- console.log('[GAME ENGINE] Stopping game and saving...');
- if (debugLogger) await debugLogger.logStep('Stopping game engine');
-
- if (!this.isRunning) {
- if (debugLogger) debugLogger.logStep('Game already stopped, ignoring stop request');
- return;
- }
-
- this.isRunning = false;
-
- // Clear game logic timer
- if (this.gameLogicTimer) {
- clearInterval(this.gameLogicTimer);
- this.gameLogicTimer = null;
- }
-
- // Clear auto-save timer
- if (this.autoSaveTimer) {
- clearInterval(this.autoSaveTimer);
- this.autoSaveTimer = null;
- }
-
- // Clear server polling timer (multiplayer mode)
- if (this.serverPollTimer) {
- clearInterval(this.serverPollTimer);
- this.serverPollTimer = null;
- }
-
- // Stop economy system timers
- if (this.systems.economy) {
- this.systems.economy.stopShopTimers();
- }
-
- console.log('[GAME ENGINE] Game stopped and saved successfully');
-
- if (debugLogger) await debugLogger.endStep('stopGame', {
- gameTime: this.gameTime,
- isRunning: this.isRunning
- });
- }
-
- startAutoSave() {
- const debugLogger = window.debugLogger;
-
- // Load saved interval or use default
- const savedInterval = localStorage.getItem('autoSaveInterval');
- this.autoSaveInterval = savedInterval ? parseInt(savedInterval) : 5;
-
- console.log(`[GAME ENGINE] Starting auto-save with ${this.autoSaveInterval} minute interval`);
- if (debugLogger) debugLogger.logStep('Starting auto-save system', {
- interval: this.autoSaveInterval,
- intervalMs: this.autoSaveInterval * 60 * 1000,
- savedInterval: savedInterval,
- isRunning: this.isRunning
- });
-
- // Clear any existing timer
- this.stopAutoSave();
-
- // Set up new timer
- this.autoSaveTimer = setInterval(async () => {
- if (debugLogger) debugLogger.logStep('Auto-save timer triggered', {
- isRunning: this.isRunning,
- paused: this.state.paused,
- gameTime: this.gameTime,
- lastAutoSave: this.lastAutoSave
- });
-
- console.log('[GAME ENGINE] Auto-save timer triggered - isRunning:', this.isRunning, 'paused:', this.state.paused);
-
- if (this.isRunning && !this.state.paused) {
- console.log('[GAME ENGINE] Auto-saving game...');
- if (debugLogger) debugLogger.logStep('Auto-saving game');
-
- try {
- await this.save();
- this.showNotification('Game auto-saved', 'info', 2000);
- console.log('[GAME ENGINE] Auto-save completed successfully');
-
- this.lastAutoSave = Date.now();
- if (debugLogger) debugLogger.logStep('Auto-save completed successfully', {
- lastAutoSave: this.lastAutoSave
- });
-
- } catch (error) {
- console.error('[GAME ENGINE] Auto-save failed:', error);
- if (debugLogger) await debugLogger.errorEvent(error, 'Auto-save');
- }
- } else {
- console.log('[GAME ENGINE] Auto-save skipped - game not running or paused');
- if (debugLogger) debugLogger.logStep('Auto-save skipped', {
- reason: this.isRunning ? 'paused' : 'not running',
- isRunning: this.isRunning,
- paused: this.state.paused
- });
- }
- }, this.autoSaveInterval * 60 * 1000); // Convert minutes to milliseconds
-
- this.lastAutoSave = Date.now();
- if (debugLogger) debugLogger.logStep('Auto-save timer started', {
- timerId: this.autoSaveTimer,
- lastAutoSave: this.lastAutoSave
- });
- }
-
- stopAutoSave() {
- const debugLogger = window.debugLogger;
-
- if (this.autoSaveTimer) {
- console.log('[GAME ENGINE] Stopping auto-save timer');
- if (debugLogger) debugLogger.logStep('Stopping auto-save timer', {
- timerId: this.autoSaveTimer,
- wasRunning: true
- });
-
- clearInterval(this.autoSaveTimer);
- this.autoSaveTimer = null;
-
- if (debugLogger) debugLogger.logStep('Auto-save timer stopped');
- } else {
- if (debugLogger) debugLogger.logStep('stopAutoSave called but no timer was active', {
- timerId: this.autoSaveTimer,
- wasRunning: false
- });
- }
- }
-
- updateAutoSaveInterval(newInterval) {
- const debugLogger = window.debugLogger;
-
- console.log(`[GAME ENGINE] Updating auto-save interval to ${newInterval} minutes`);
- if (debugLogger) debugLogger.logStep('Updating auto-save interval', {
- oldInterval: this.autoSaveInterval,
- newInterval: newInterval,
- isRunning: this.isRunning
- });
-
- this.autoSaveInterval = newInterval;
-
- // Save to localStorage
- localStorage.setItem('autoSaveInterval', newInterval.toString());
-
- // Restart auto-save with new interval if game is running
- if (this.isRunning) {
- if (debugLogger) debugLogger.logStep('Restarting auto-save with new interval');
- this.startAutoSave();
- }
-
- if (debugLogger) debugLogger.logStep('Auto-save interval updated successfully', {
- currentInterval: this.autoSaveInterval,
- savedToStorage: true
- });
- }
-
- start() {
- const debugLogger = window.debugLogger;
-
- if (this.isRunning) {
- if (debugLogger) debugLogger.log('GameEngine.start() called but game is already running', {
- isRunning: this.isRunning,
- gameTime: this.gameTime
- });
- return;
- }
-
- if (debugLogger) debugLogger.logStep('Starting game engine', {
- gameLogicInterval: this.gameLogicInterval
- });
-
- this.isRunning = true;
- this.lastUpdate = Date.now();
-
- // Start game logic timer (completely independent of frame rate)
- console.log('[GAME ENGINE] Starting game logic timer with interval:', this.gameLogicInterval);
- this.gameLogicTimer = setInterval(() => {
- this.updateGameLogic();
- }, this.gameLogicInterval);
-
- // Start auto-save
- this.startAutoSave();
-
- // Check and apply any pending server data
- if (this.checkAndApplyPendingServerData) {
- this.checkAndApplyPendingServerData();
- }
-
- console.log('[GAME ENGINE] Game engine started');
- if (debugLogger) debugLogger.logStep('Game engine started successfully', {
- gameLogicInterval: this.gameLogicInterval,
- autoSaveInterval: this.autoSaveInterval
- });
- }
-
- updateGameLogic() {
- const debugLogger = window.debugLogger;
-
- // Use fixed 1-second interval for all updates
- const fixedDelta = 1000; // 1 second in milliseconds
-
- console.log('[GAME ENGINE] Game logic update called - adding', fixedDelta, 'ms to playtime');
-
- if (this.state.paused) {
- if (debugLogger) debugLogger.logStep('Game logic update called but game is paused', {
- gameTime: this.gameTime,
- fixedDelta: fixedDelta
- });
- return;
- }
-
- this.gameTime += fixedDelta;
-
-
- // Log update performance every 300 frames (approximately 5 seconds)
- if (debugLogger && this.gameTime % 15000 === 0) { // Every 15 seconds of game time
- debugLogger.logStep('Game logic update cycle', {
- gameTime: this.gameTime,
- fixedDelta: fixedDelta,
- isRunning: this.isRunning,
- paused: this.state.paused,
- currentTab: this.state.currentTab
- });
- }
-
- // Update player play time with fixed delta
- if (this.systems.player && this.systems.player.updatePlayTime) {
- console.log('[GAME ENGINE] Before update - player playTime:', this.systems.player.stats.playTime);
- this.systems.player.updatePlayTime(fixedDelta);
- console.log('[GAME ENGINE] After update - player playTime:', this.systems.player.stats.playTime);
- }
-
- // Update all systems with fixed delta
- for (const [name, system] of Object.entries(this.systems)) {
- if (system && typeof system.update === 'function') {
- try {
- system.update(fixedDelta);
- } catch (error) {
- console.error(`[GAME ENGINE] Error updating ${name} system:`, error);
- if (debugLogger) debugLogger.errorEvent(error, `Update ${name} system`);
- }
- }
- }
-
- // Update UI displays (money, gems, energy) after system updates
- // Only update UI if in multiplayer mode or if game is actively running
- const shouldUpdateUI = this.systems && this.systems.ui &&
- (window.smartSaveManager?.isMultiplayer || this.isRunning);
-
- if (shouldUpdateUI) {
- console.log('[GAME ENGINE] Updating UI displays');
- try {
- this.systems.ui.updateUI();
- console.log('[GAME ENGINE] UI update completed');
- } catch (error) {
- console.error('[GAME ENGINE] Error updating UI:', error);
- }
- }
-
- // Only emit UI update event if in multiplayer mode or game is actively running
- const shouldEmitUIUpdate = window.smartSaveManager?.isMultiplayer || this.isRunning;
-
- if (shouldEmitUIUpdate) {
- this.emitUIUpdateEvent('full');
- }
-
- // Emit game updated event
- this.emit('gameUpdated', { gameTime: this.gameTime });
- }
-
- update(deltaTime) {
- // Legacy method - game logic now handled by updateGameLogic()
- // This method kept for compatibility but doesn't process game systems
- }
-
- shouldUpdateGUI() {
- // Use centralized UI update control from UIManager
- if (this.systems && this.systems.ui && this.systems.ui.shouldUpdateUI) {
- const currentTime = Date.now();
- if (currentTime - this.lastGUIUpdate >= this.guiUpdateInterval) {
- this.lastGUIUpdate = currentTime;
- return true;
- }
- }
- return false;
- }
-
- updateGUI() {
- const debugLogger = window.debugLogger;
-
- console.log('[GAME ENGINE] Updating GUI');
-
- // Update UI displays (money, gems, energy) after system updates
- // Only update UI if in multiplayer mode or if game is actively running
- const shouldUpdateUI = this.systems && this.systems.ui &&
- (window.smartSaveManager?.isMultiplayer || this.isRunning);
-
- if (shouldUpdateUI) {
- console.log('[GAME ENGINE] Updating UI displays');
- try {
- this.systems.ui.updateUI();
- console.log('[GAME ENGINE] UI update completed');
- } catch (error) {
- console.error('[GAME ENGINE] Error updating UI:', error);
- }
- } else {
- // Skip UI updates when not in multiplayer and game is not actively running
- if (this.systems && this.systems.ui) {
- console.log('[GAME ENGINE] Skipping GUI updates - not in multiplayer mode');
- }
- }
- }
-
- // Event system
- on(event, callback) {
- if (!this.eventListeners.has(event)) {
- this.eventListeners.set(event, []);
- }
- this.eventListeners.get(event).push(callback);
- }
-
- emit(event, data) {
- if (this.eventListeners.has(event)) {
- this.eventListeners.get(event).forEach(callback => {
- try {
- callback(data);
- } catch (error) {
- console.error(`[GAME ENGINE] Error in event listener for ${event}:`, error);
- }
- });
- }
-
- // Also dispatch as DOM event for UIManager
- this.dispatchEvent(new CustomEvent(event, { detail: data }));
- }
-
- emitUIUpdateEvent(type, data = {}) {
- const eventData = {
- type: type,
- timestamp: Date.now(),
- ...data
- };
-
- console.log('[GAME ENGINE] Emitting UI update event:', eventData);
- this.emit('uiUpdate', eventData);
- }
-
- // Notification system
- async showNotification(message, type = 'info', duration = 3000) {
- const logger = window.logger;
- if (logger) await logger.playerAction('Notification', { message, type, duration });
-
- const notification = {
- id: Date.now(),
- message,
- type,
- duration,
- timestamp: Date.now()
- };
-
- this.state.notifications.push(notification);
-
- // Auto-remove notification after duration
- setTimeout(() => {
- this.removeNotification(notification.id);
- }, duration);
-
- // Update UI
- if (this.systems.ui) {
- // UI updates handled by individual systems
- }
- }
-
- removeNotification(id) {
- this.state.notifications = this.state.notifications.filter(notification => notification.id !== id);
- }
-
- processNotifications() {
- const now = Date.now();
- this.state.notifications = this.state.notifications.filter(
- notification => now - notification.timestamp < notification.duration
- );
- }
-
- // Save/Load system
- async save() {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- console.log('[GAME ENGINE] Save method called');
- if (logger) await logger.info('Saving game');
- // if (debugLogger) await debugLogger.startStep('saveGame');
-
- try {
- // if (debugLogger) await debugLogger.logStep('Collecting save data from systems');
- console.log('[GAME ENGINE] Collecting save data from systems...');
-
- const saveData = {
- player: this.systems.player.save(),
- inventory: this.systems.inventory.save(),
- economy: this.systems.economy.save(),
- idleSystem: this.systems.idleSystem.save(),
- dungeonSystem: this.systems.dungeonSystem.save(),
- skillSystem: this.systems.skillSystem.save(),
- baseSystem: this.systems.baseSystem.save(),
- questSystem: this.systems.questSystem.save(),
- gameTime: this.gameTime,
- lastSave: Date.now()
- };
-
- // if (debugLogger) await debugLogger.logStep('Save data collected', {
- // playerLevel: saveData.player?.stats?.level,
- // gameTime: this.gameTime,
- // saveDataSize: JSON.stringify(saveData).length,
- // });
-
- // console.log('[GAME ENGINE] Save data collected, player level:', saveData.player?.stats?.level);
- // console.log('[GAME ENGINE] Save slot info:', this.saveSlotInfo);}
-
- // Check if we should save to server (multiplayer mode)
- if (window.smartSaveManager && window.smartSaveManager.isMultiplayer) {
- console.log('[GAME ENGINE] Saving to server in multiplayer mode');
- if (debugLogger) await debugLogger.logStep('Saving to server in multiplayer mode');
-
- try {
- const serverSaveResult = await window.smartSaveManager.savePlayerData(saveData);
- if (serverSaveResult) {
- console.log('[GAME ENGINE] Game saved successfully to server');
- if (debugLogger) await debugLogger.logStep('Game saved successfully to server');
- } else {
- console.warn('[GAME ENGINE] Server save failed, falling back to local storage');
- if (debugLogger) await debugLogger.warn('Server save failed, using local fallback');
-
- // Fallback to localStorage
- const saveKey = `gso_save_slot_${this.saveSlotInfo?.slot || 1}`;
- localStorage.setItem(saveKey, JSON.stringify(saveData));
- }
- } catch (error) {
- console.error('[GAME ENGINE] Error saving to server:', error);
- if (debugLogger) await debugLogger.errorEvent(error, 'Server Save');
-
- // Fallback to localStorage
- const saveKey = `gso_save_slot_${this.saveSlotInfo?.slot || 1}`;
- localStorage.setItem(saveKey, JSON.stringify(saveData));
- console.log('[GAME ENGINE] Error fallback: Game saved to localStorage');
- }
- } else {
- // Local save logic (existing code)
- const isLocalMode = window.liveMainMenu && window.liveMainMenu.isLocalMode;
-
- if (isLocalMode) {
- console.log('[GAME ENGINE] Using localStorage for local mode save');
- if (debugLogger) await debugLogger.logStep('Saving via localStorage for local mode');
-
- try {
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- const saveString = JSON.stringify(saveData);
-
- // Save to localStorage
- localStorage.setItem(saveKey, saveString);
-
- console.log('[GAME ENGINE] Game saved successfully to localStorage');
- console.log('[GAME ENGINE] Save key:', saveKey);
- console.log('[GAME ENGINE] Save data size:', saveString.length, 'characters');
- console.log('[GAME ENGINE] Current localStorage keys:', Object.keys(localStorage));
-
- // Verify save exists
- const verifySave = localStorage.getItem(saveKey);
- console.log('[GAME ENGINE] Save verification:', !!verifySave ? 'SUCCESS' : 'FAILED');
-
- if (debugLogger) await debugLogger.logStep('Game saved successfully to localStorage', {
- saveKey,
- saveDataSize: saveString.length
- });
-
- // Emit save event
- this.emit('gameSaved', { slot: this.saveSlotInfo.slot, saveData });
- // if (debugLogger) debugLogger.logStep('Game saved event emitted');
-
- } catch (error) {
- console.error('[GAME ENGINE] Failed to save to localStorage:', error);
- if (debugLogger) await debugLogger.errorEvent(error, 'LocalStorage Save');
- throw error;
- }
- } else {
- // Use file system if available, otherwise localStorage (original logic)
- if (this.saveSlotInfo && this.saveSlotInfo.useFileSystem) {
- if (window.electronAPI) {
- // console.log('[GAME ENGINE] Using Electron API to save game');
- // if (debugLogger) await debugLogger.logStep('Saving via Electron API', {
- // slot: this.saveSlotInfo.slot,
- // useFileSystem: true
- // });
-
- try {
- // Save through Electron API
- const result = await window.electronAPI.saveGame(this.saveSlotInfo.slot, saveData);
- if (result.success) {
- // console.log('[GAME ENGINE] Game saved successfully via Electron API');
- if (debugLogger) await debugLogger.logStep('Game saved successfully via Electron API', {
- slot: this.saveSlotInfo.slot,
- result: result
- });
-
- // Emit save event
- this.emit('gameSaved', { slot: this.saveSlotInfo.slot, saveData });
- // if (debugLogger) debugLogger.logStep('Game saved event emitted');
-
- } else {
- console.error('[GAME ENGINE] Failed to save via Electron API:', result.error);
- if (debugLogger) await debugLogger.errorEvent(new Error(result.error), 'Electron API Save');
-
- // Fallback to localStorage
- if (debugLogger) await debugLogger.logStep('Falling back to localStorage');
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- localStorage.setItem(saveKey, JSON.stringify(saveData));
- console.log('[GAME ENGINE] Fallback: Game saved to localStorage with key:', saveKey);
- if (debugLogger) await debugLogger.logStep('Game saved to localStorage fallback', {
- saveKey,
- dataSize: JSON.stringify(saveData).length
- });
- }
- } catch (error) {
- console.error('[GAME ENGINE] Electron API save error:', error);
- if (debugLogger) await debugLogger.errorEvent(error, 'Electron API Save');
-
- // Fallback to localStorage
- if (debugLogger) await debugLogger.logStep('Falling back to localStorage due to error');
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- localStorage.setItem(saveKey, JSON.stringify(saveData));
- console.log('[GAME ENGINE] Error fallback: Game saved to localStorage with key:', saveKey);
- }
- } else {
- console.warn('[GAME ENGINE] Electron API not available, using localStorage');
- if (debugLogger) await debugLogger.warn('Electron API not available, using localStorage');
-
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- localStorage.setItem(saveKey, JSON.stringify(saveData));
- console.log('[GAME ENGINE] Game saved to localStorage with key:', saveKey);
- if (debugLogger) await debugLogger.logStep('Game saved to localStorage', {
- saveKey,
- dataSize: JSON.stringify(saveData).length
- });
- }
- } else {
- // LocalStorage fallback
- if (debugLogger) await debugLogger.logStep('Using localStorage save');
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- localStorage.setItem(saveKey, JSON.stringify(saveData));
- console.log('[GAME ENGINE] Game saved to localStorage with key:', saveKey);
- if (debugLogger) await debugLogger.logStep('Game saved to localStorage', {
- saveKey,
- dataSize: JSON.stringify(saveData).length
- });
- }
- }
-
- if (logger) await logger.info('Game saved successfully');
-
- // Check if we should also save to server (multiplayer mode)
- if (window.smartSaveManager && window.smartSaveManager.isMultiplayer) {
- console.log('[GAME ENGINE] Also saving to server in multiplayer mode');
- try {
- await window.smartSaveManager.savePlayerData(saveData);
- console.log('[GAME ENGINE] Game saved successfully to server');
- } catch (error) {
- console.error('[GAME ENGINE] Error saving to server:', error);
- // Don't throw error - local save was successful
- }
- }
-
- // if (debugLogger) await debugLogger.endStep('saveGame', {
- // gameTime: this.gameTime,
- // saveSlot: this.saveSlotInfo?.slot,
- // success: true
- // });
-
- }
- } catch (error) {
- console.error('[GAME ENGINE] Save failed:', error);
- if (logger) await logger.errorEvent(error, 'Game Save');
- if (debugLogger) await debugLogger.errorEvent(error, 'Game Save');
- throw error;
- }
- }
-
- async loadPlayerData(playerData) {
- console.log('[GAME ENGINE] Loading server player data into game');
-
- try {
- // Apply player stats
- if (playerData.stats && this.systems && this.systems.player) {
- console.log('[GAME ENGINE] Applying player stats:', playerData.stats);
- this.systems.player.load(playerData.stats);
- }
-
- // Apply inventory
- if (playerData.inventory && this.systems && this.systems.inventory) {
- console.log('[GAME ENGINE] Applying player inventory:', playerData.inventory);
- this.systems.inventory.load(playerData.inventory);
- }
-
- // Apply ship data
- if (playerData.ship && this.systems && this.systems.ship) {
- console.log('[GAME ENGINE] Applying player ship:', playerData.ship);
- this.systems.ship.load(playerData.ship);
- }
-
- // Apply base data
- if (playerData.base && this.systems && this.systems.base) {
- console.log('[GAME ENGINE] Applying player base:', playerData.base);
- this.systems.base.load(playerData.base);
- }
-
- // Apply quest data
- if (playerData.quests && this.systems && this.systems.quests) {
- console.log('[GAME ENGINE] Applying player quests:', playerData.quests);
- this.systems.quests.load(playerData.quests);
- }
-
- // Apply skill data
- if (playerData.skills && this.systems && this.systems.skills) {
- console.log('[GAME ENGINE] Applying player skills:', playerData.skills);
- this.systems.skills.load(playerData.skills);
- }
-
- // Apply idle system data
- if (playerData.idleSystem && this.systems && this.systems.idleSystem) {
- console.log('[GAME ENGINE] Applying player idle system:', playerData.idleSystem);
- this.systems.idleSystem.load(playerData.idleSystem);
- }
-
- // Apply dungeon system data
- if (playerData.dungeonSystem && this.systems && this.systems.dungeonSystem) {
- console.log('[GAME ENGINE] Applying player dungeon system:', playerData.dungeonSystem);
- this.systems.dungeonSystem.load(playerData.dungeonSystem);
- }
-
- // Apply crafting system data
- if (playerData.craftingSystem && this.systems && this.systems.craftingSystem) {
- console.log('[GAME ENGINE] Applying player crafting system:', playerData.craftingSystem);
- this.systems.craftingSystem.load(playerData.craftingSystem);
- }
-
- // Apply game time
- if (playerData.gameTime !== undefined) {
- this.gameTime = playerData.gameTime;
- console.log('[GAME ENGINE] Game time restored:', this.gameTime);
- }
-
- console.log('[GAME ENGINE] Server player data applied successfully');
-
- // Show notification to user
- if (this.showNotification) {
- this.showNotification(`Welcome back! Level ${playerData.stats?.level || 1}`, 'success', 3000);
- }
-
- } catch (error) {
- console.error('[GAME ENGINE] Error loading server player data:', error);
- if (this.showNotification) {
- this.showNotification('Failed to load server data!', 'error', 3000);
- }
- }
- }
-
- async load() {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- console.log('[GAME ENGINE] Load method called');
- if (logger) await logger.info('Loading game');
- if (debugLogger) await debugLogger.startStep('loadGame');
-
- try {
- if (debugLogger) await debugLogger.logStep('Loading save data', {
- saveSlot: this.saveSlotInfo?.slot,
- useFileSystem: this.saveSlotInfo?.useFileSystem
- });
-
- let saveData;
-
- // Use file system if available, otherwise localStorage
- if (this.saveSlotInfo && this.saveSlotInfo.useFileSystem && window.electronAPI) {
- console.log('[GAME ENGINE] Loading via Electron API');
- if (debugLogger) await debugLogger.logStep('Loading via Electron API');
-
- const result = await window.electronAPI.loadGame(this.saveSlotInfo.slot);
- if (result.success) {
- saveData = result.data;
- console.log('[GAME ENGINE] Game loaded successfully via Electron API');
- if (debugLogger) await debugLogger.logStep('Game loaded via Electron API', {
- slot: this.saveSlotInfo.slot,
- dataSize: JSON.stringify(saveData).length
- });
- } else {
- console.error('[GAME ENGINE] Failed to load via Electron API:', result.error);
- if (debugLogger) await debugLogger.errorEvent(new Error(result.error), 'Electron API Load');
- throw new Error(result.error);
- }
- } else {
- // LocalStorage fallback
- console.log('[GAME ENGINE] Loading from localStorage');
- if (debugLogger) await debugLogger.logStep('Loading from localStorage');
-
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- const saveString = localStorage.getItem(saveKey);
-
- if (saveString) {
- try {
- saveData = JSON.parse(saveString);
- console.log('[GAME ENGINE] Game loaded from localStorage');
- if (debugLogger) await debugLogger.logStep('Game loaded from localStorage', {
- saveKey,
- dataSize: saveString.length
- });
- } catch (parseError) {
- console.error('[GAME ENGINE] Failed to parse save data:', parseError);
- if (debugLogger) await debugLogger.errorEvent(parseError, 'Parse Save Data');
- throw new Error('Corrupted save data');
- }
- } else {
- console.warn('[GAME ENGINE] No save data found in localStorage');
- if (debugLogger) await debugLogger.warn('No save data found in localStorage', { saveKey });
- throw new Error('No save data found');
- }
- }
-
- // if (debugLogger) await debugLogger.logStep('Applying save data to systems');
-
- // Apply save data to systems
- if (saveData.player && this.systems.player) {
- console.log('[GAME ENGINE] Loading player data...');
- this.systems.player.load(saveData.player);
- if (debugLogger) await debugLogger.logStep('Player data loaded', {
- level: saveData.player?.stats?.level,
- experience: saveData.player?.stats?.experience
- });
- }
-
- if (saveData.inventory && this.systems.inventory) {
- console.log('[GAME ENGINE] Loading inventory data...');
- this.systems.inventory.load(saveData.inventory);
- if (debugLogger) await debugLogger.logStep('Inventory data loaded', {
- itemCount: saveData.inventory.items?.length || 0
- });
- }
-
- if (saveData.economy && this.systems.economy) {
- console.log('[GAME ENGINE] Loading economy data...');
- this.systems.economy.load(saveData.economy);
- if (debugLogger) await debugLogger.logStep('Economy data loaded', {
- credits: saveData.economy.credits,
- gems: saveData.economy.gems
- });
- }
-
- if (saveData.gameTime) {
- this.gameTime = saveData.gameTime;
- if (debugLogger) await debugLogger.logStep('Game time restored', {
- gameTime: this.gameTime
- });
- }
-
- // Emit load event
- this.emit('gameLoaded', { saveData });
- if (debugLogger) debugLogger.logStep('Game loaded event emitted');
-
- if (logger) await logger.info('Game loaded successfully');
- if (debugLogger) await debugLogger.endStep('loadGame', {
- gameTime: this.gameTime,
- saveSlot: this.saveSlotInfo?.slot,
- success: true
- });
-
- } catch (error) {
- console.error('[GAME ENGINE] Load failed:', error);
- if (logger) await logger.errorEvent(error, 'Game Load');
- if (debugLogger) await debugLogger.errorEvent(error, 'Game Load');
- throw error;
- }
- }
-
- async newGame() {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- console.log('[GAME ENGINE] Starting new game initialization');
- if (logger) await logger.info('Starting new game');
- if (debugLogger) await debugLogger.startStep('newGame');
-
- try {
- // For new games, we need to properly initialize systems with default data
- if (debugLogger) await debugLogger.logStep('Initializing systems for new game');
-
- // Initialize inventory with starting items
- if (this.systems.inventory) {
- console.log('[GAME ENGINE] Initializing inventory with starting items');
- if (debugLogger) await debugLogger.logStep('Initializing inventory with starting items');
- await this.systems.inventory.initialize();
- }
-
- // Initialize DungeonSystem for new game
- if (this.systems.dungeonSystem) {
- console.log('[GAME ENGINE] Initializing DungeonSystem for new game...');
- if (debugLogger) await debugLogger.logStep('Initializing DungeonSystem for new game');
- try {
- await this.systems.dungeonSystem.initialize();
- console.log('[GAME ENGINE] DungeonSystem initialized successfully for new game');
- if (debugLogger) await debugLogger.logStep('DungeonSystem initialized successfully for new game');
- } catch (dungeonError) {
- console.error('[GAME ENGINE] DungeonSystem initialization failed for new game:', dungeonError);
- if (debugLogger) await debugLogger.errorEvent(dungeonError, 'DungeonSystem initialization failed for new game');
- }
- } else {
- console.log('[GAME ENGINE] DungeonSystem not found in systems!');
- }
-
- if (this.systems.player) {
- console.log('[GAME ENGINE] Resetting player to initial state');
- if (debugLogger) await debugLogger.logStep('Resetting player to initial state');
- this.systems.player.resetToLevel1();
- console.log('[GAME ENGINE] Player reset completed');
- if (debugLogger) await debugLogger.logStep('Player reset completed', {
- level: this.systems.player.stats.level,
- experience: this.systems.player.stats.experience,
- playTime: this.systems.player.stats.playTime
- });
-
- console.log('[GAME ENGINE] Setting up new player');
- if (debugLogger) await debugLogger.logStep('Setting up new player');
- this.systems.player.setupNewPlayer();
- console.log('[GAME ENGINE] Player setup completed');
- if (debugLogger) await debugLogger.logStep('Player setup completed', {
- level: this.systems.player.stats.level,
- experience: this.systems.player.stats.experience
- });
- } else {
- console.error('[GAME ENGINE] Player system not available');
- if (debugLogger) await debugLogger.error('Player system not available');
- }
-
- if (debugLogger) await debugLogger.logStep('Resetting economy system');
- console.log('[GAME ENGINE] Step 2: Resetting economy system');
-
- if (this.systems.economy) {
- console.log('[GAME ENGINE] Calling economy.reset()');
- if (debugLogger) await debugLogger.logStep('Calling economy.reset()');
- this.systems.economy.reset();
- console.log('[GAME ENGINE] Economy reset completed');
- if (debugLogger) await debugLogger.logStep('Economy reset completed', {
- credits: this.systems.economy.credits,
- gems: this.systems.economy.gems
- });
- } else {
- console.error('[GAME ENGINE] Economy system not available');
- if (debugLogger) await debugLogger.error('Economy system not available');
- }
-
- // Skip inventory reset - initialize() already handles proper setup
- if (debugLogger) await debugLogger.logStep('Skipping inventory reset - already initialized');
- console.log('[GAME ENGINE] Skipping inventory reset - already initialized with starting items');
-
- if (this.systems.inventory) {
- if (debugLogger) await debugLogger.logStep('Inventory already initialized', {
- itemCount: this.systems.inventory.items.length
- });
- } else {
- console.error('[GAME ENGINE] Inventory system not available');
- if (debugLogger) await debugLogger.error('Inventory system not available');
- }
-
- if (debugLogger) await debugLogger.logStep('Resetting quest system');
- console.log('[GAME ENGINE] Step 4: Resetting quest system');
-
- if (this.systems.questSystem) {
- console.log('[GAME ENGINE] Calling questSystem.reset()');
- if (debugLogger) await debugLogger.logStep('Calling questSystem.reset()');
- this.systems.questSystem.reset();
- console.log('[GAME ENGINE] Quest system reset completed');
- if (debugLogger) await debugLogger.logStep('Quest system reset completed');
-
- // Activate the tutorial quest for new games
- console.log('[GAME ENGINE] Activating tutorial quest for new game');
- if (debugLogger) await debugLogger.logStep('Activating tutorial quest');
- this.systems.questSystem.startQuest('tutorial_complete');
- console.log('[GAME ENGINE] Tutorial quest activated');
- if (debugLogger) await debugLogger.logStep('Tutorial quest activated');
- } else {
- console.error('[GAME ENGINE] Quest system not available');
- if (debugLogger) await debugLogger.error('Quest system not available');
- }
-
- if (debugLogger) await debugLogger.logStep('Resetting game time');
- console.log('[GAME ENGINE] Step 5: Resetting game time');
- this.gameTime = 0;
- console.log('[GAME ENGINE] Game time reset to 0');
- if (debugLogger) await debugLogger.logStep('Game time reset', { gameTime: this.gameTime });
-
- if (logger) await logger.info('New game initialized successfully');
- if (debugLogger) await debugLogger.endStep('newGame', {
- gameTime: this.gameTime,
- playerLevel: this.systems.player?.stats.level,
- success: true
- });
-
- } catch (error) {
- console.error('[GAME ENGINE] Failed to initialize new game:', error);
- if (logger) await logger.errorEvent(error, 'New Game Initialization');
- if (debugLogger) await debugLogger.errorEvent(error, 'New Game Initialization');
- throw error;
- }
- }
-
- // Utility methods
- formatNumber(num) {
- if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B';
- if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M';
- if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K';
- return num.toString();
- }
-
- formatTime(seconds) {
- const hours = Math.floor(seconds / 3600);
- const minutes = Math.floor((seconds % 3600) / 60);
- const secs = Math.floor(seconds % 60);
-
- if (hours > 0) {
- return `${hours}h ${minutes}m ${secs}s`;
- } else if (minutes > 0) {
- return `${minutes}m ${secs}s`;
- } else {
- return `${secs}s`;
- }
- }
-
- toggleDebugConsole() {
- const debugLogger = window.debugLogger;
- // if (debugLogger) debugLogger.logStep('Toggle debug console requested');
-
- // Implementation would go here
- // console.log('[GAME ENGINE] Debug console toggle requested');
- }
-
- getPerformanceStats() {
- const debugLogger = window.debugLogger;
-
- const stats = {
- gameTime: this.gameTime,
- isRunning: this.isRunning,
- lastUpdate: this.lastUpdate,
- memory: null
- };
-
- // Add memory info if available
- if (window.performance && window.performance.memory) {
- stats.memory = {
- used: window.performance.memory.usedJSHeapSize,
- total: window.performance.memory.totalJSHeapSize,
- limit: window.performance.memory.jsHeapSizeLimit
- };
- }
-
- // if (debugLogger) debugLogger.logStep('Performance stats requested', stats);
-
- return stats;
- }
-
- // Simple loadPlayerData method for server data integration
- async loadPlayerData(playerData) {
- console.log('[GAME ENGINE] Loading server player data');
-
- try {
- // Apply basic player stats
- if (playerData.stats && this.systems && this.systems.player) {
- this.systems.player.load(playerData.stats);
- console.log('[GAME ENGINE] Applied player stats:', playerData.stats);
- }
-
- // Apply inventory
- if (playerData.inventory && this.systems && this.systems.inventory) {
- this.systems.inventory.load(playerData.inventory);
- console.log('[GAME ENGINE] Applied inventory');
- }
-
- // Apply ship data
- if (playerData.ship && this.systems && this.systems.ship) {
- this.systems.ship.load(playerData.ship);
- console.log('[GAME ENGINE] Applied ship data');
- }
-
- // Apply base data
- if (playerData.base && this.systems && this.systems.base) {
- this.systems.base.load(playerData.base);
- console.log('[GAME ENGINE] Applied base data');
- }
-
- // Show notification
- if (this.showNotification) {
- this.showNotification(`Welcome back! Level ${playerData.stats?.level || 1}`, 'success', 3000);
- }
-
- console.log('[GAME ENGINE] Server player data loaded successfully');
-
- } catch (error) {
- console.error('[GAME ENGINE] Error loading server player data:', error);
- if (this.showNotification) {
- this.showNotification('Failed to load server data!', 'error', 3000);
- }
- }
- }
-}
-
-// Global game instance
-let game = null;
-
-// Export GameEngine to global scope
-if (typeof window !== 'undefined') {
- window.GameEngine = GameEngine;
-}
diff --git a/Client-Server/js/data/GameData.js b/Client-Server/js/data/GameData.js
deleted file mode 100644
index 9ce79d7..0000000
--- a/Client-Server/js/data/GameData.js
+++ /dev/null
@@ -1,570 +0,0 @@
-/**
- * Galaxy Strike Online - Game Data
- * Static game data, constants, and configuration
- */
-
-// Game configuration
-const GAME_CONFIG = {
- version: '1.0.0',
- name: 'Galaxy Strike Online',
- maxLevel: 100,
- saveInterval: 30000, // 30 seconds
- notificationDuration: 3000,
- maxNotifications: 5
-};
-
-// Player defaults
-const PLAYER_DEFAULTS = {
- level: 1,
- experience: 0,
- skillPoints: 0,
- credits: 1000,
- gems: 10,
- health: 100,
- maxHealth: 100,
- energy: 100,
- maxEnergy: 100,
- attack: 10,
- defense: 5,
- speed: 10,
- criticalChance: 0.05,
- criticalDamage: 1.5
-};
-
-// Experience requirements
-const EXPERIENCE_TABLE = [];
-for (let i = 1; i <= 100; i++) {
- EXPERIENCE_TABLE[i] = Math.floor(100 * Math.pow(1.5, i - 1));
-}
-
-// Item rarities with colors and multipliers
-const ITEM_RARITIES = {
- common: {
- name: 'Common',
- color: '#888888',
- multiplier: 1.0,
- dropChance: 0.60
- },
- uncommon: {
- name: 'Uncommon',
- color: '#00ff00',
- multiplier: 1.2,
- dropChance: 0.25
- },
- rare: {
- name: 'Rare',
- color: '#0088ff',
- multiplier: 1.5,
- dropChance: 0.10
- },
- epic: {
- name: 'Epic',
- color: '#8833ff',
- multiplier: 2.0,
- dropChance: 0.04
- },
- legendary: {
- name: 'Legendary',
- color: '#ff8800',
- multiplier: 3.0,
- dropChance: 0.01
- }
-};
-
-// Enemy types and stats
-const ENEMY_TEMPLATES = {
- space_pirate: {
- name: 'Space Pirate',
- health: 25,
- attack: 10,
- defense: 3,
- speed: 8,
- experience: 15,
- credits: 12,
- rarity: 'common'
- },
- alien_guardian: {
- name: 'Alien Guardian',
- health: 50,
- attack: 8,
- defense: 5,
- speed: 6,
- experience: 25,
- credits: 15,
- rarity: 'common'
- },
- mining_drone: {
- name: 'Mining Drone',
- health: 20,
- attack: 8,
- defense: 3,
- speed: 5,
- experience: 12,
- credits: 8,
- rarity: 'common'
- },
- security_drone: {
- name: 'Security Drone',
- health: 35,
- attack: 14,
- defense: 4,
- speed: 10,
- experience: 22,
- credits: 15,
- rarity: 'uncommon'
- },
- pirate_captain: {
- name: 'Pirate Captain',
- health: 40,
- attack: 15,
- defense: 6,
- speed: 12,
- experience: 30,
- credits: 20,
- rarity: 'uncommon'
- },
- crystal_golem: {
- name: 'Crystal Golem',
- health: 80,
- attack: 6,
- defense: 10,
- speed: 4,
- experience: 35,
- credits: 25,
- rarity: 'rare'
- },
- corrupted_ai: {
- name: 'Corrupted AI',
- health: 60,
- attack: 20,
- defense: 2,
- speed: 15,
- experience: 40,
- credits: 30,
- rarity: 'rare'
- },
- energy_being: {
- name: 'Energy Being',
- health: 55,
- attack: 22,
- defense: 3,
- speed: 18,
- experience: 45,
- credits: 35,
- rarity: 'epic'
- },
- quantum_entity: {
- name: 'Quantum Entity',
- health: 70,
- attack: 35,
- defense: 5,
- speed: 20,
- experience: 60,
- credits: 50,
- rarity: 'legendary'
- }
-};
-
-// Dungeon configurations
-const DUNGEON_CONFIGS = {
- alien_ruins: {
- name: 'Alien Ruins',
- description: 'Ancient alien structures filled with mysterious technology',
- difficulty: 'medium',
- minLevel: 3,
- roomCount: [5, 8],
- enemyTypes: ['alien_guardian', 'ancient_drone', 'crystal_golem'],
- rewardMultiplier: 1.2,
- energyCost: 20
- },
- pirate_lair: {
- name: 'Pirate Lair',
- description: 'Dangerous pirate hideouts with valuable loot',
- difficulty: 'easy',
- minLevel: 1,
- roomCount: [4, 6],
- enemyTypes: ['space_pirate', 'pirate_captain', 'defense_turret'],
- rewardMultiplier: 1.0,
- energyCost: 15
- },
- corrupted_vault: {
- name: 'Corrupted AI Vault',
- description: 'Malfunctioning AI facilities with corrupted security',
- difficulty: 'hard',
- minLevel: 5,
- roomCount: [6, 9],
- enemyTypes: ['security_drone', 'corrupted_ai', 'virus_program'],
- rewardMultiplier: 1.5,
- energyCost: 25
- },
- asteroid_mine: {
- name: 'Asteroid Mine',
- description: 'Abandoned mining facilities in asteroid fields',
- difficulty: 'easy',
- minLevel: 2,
- roomCount: [4, 7],
- enemyTypes: ['mining_drone', 'rock_creature', 'explosive_asteroid'],
- rewardMultiplier: 0.8,
- energyCost: 10
- },
- nebula_anomaly: {
- name: 'Nebula Anomaly',
- description: 'Strange energy anomalies in deep space',
- difficulty: 'extreme',
- minLevel: 8,
- roomCount: [7, 10],
- enemyTypes: ['energy_being', 'phase_shifter', 'quantum_entity'],
- rewardMultiplier: 2.0,
- energyCost: 30
- }
-};
-
-// Skill definitions
-const SKILL_DEFINITIONS = {
- combat: {
- weapons_mastery: {
- name: 'Weapons Mastery',
- description: 'Increases weapon damage and critical chance',
- maxLevel: 10,
- experiencePerLevel: 100,
- effects: {
- attack: 2,
- criticalChance: 0.01
- },
- icon: 'fa-sword'
- },
- shield_techniques: {
- name: 'Shield Techniques',
- description: 'Improves defense and energy efficiency',
- maxLevel: 10,
- experiencePerLevel: 100,
- effects: {
- defense: 2,
- maxEnergy: 5
- },
- icon: 'fa-shield-alt'
- },
- piloting: {
- name: 'Piloting',
- description: 'Enhances speed and evasion',
- maxLevel: 10,
- experiencePerLevel: 100,
- effects: {
- speed: 2,
- criticalChance: 0.005
- },
- icon: 'fa-rocket'
- }
- },
- science: {
- energy_manipulation: {
- name: 'Energy Manipulation',
- description: 'Better energy control and regeneration',
- maxLevel: 10,
- experiencePerLevel: 100,
- effects: {
- maxEnergy: 10,
- energyRegeneration: 0.1
- },
- icon: 'fa-bolt'
- },
- alien_technology: {
- name: 'Alien Technology',
- description: 'Understanding and using alien artifacts',
- maxLevel: 10,
- experiencePerLevel: 150,
- effects: {
- findRarity: 0.05,
- itemValue: 0.1
- },
- icon: 'fa-atom'
- }
- },
- crafting: {
- weapon_crafting: {
- name: 'Weapon Crafting',
- description: 'Create and upgrade weapons',
- maxLevel: 10,
- experiencePerLevel: 100,
- effects: {
- craftingBonus: 0.1,
- weaponStats: 0.05
- },
- icon: 'fa-hammer'
- },
- armor_forging: {
- name: 'Armor Forging',
- description: 'Forge protective armor and shields',
- maxLevel: 10,
- experiencePerLevel: 100,
- effects: {
- craftingBonus: 0.1,
- armorStats: 0.05
- },
- icon: 'fa-anvil'
- }
- }
-};
-
-// Shop items
-const SHOP_ITEMS = {
- ships: [
- {
- id: 'fighter_mk1',
- name: 'Fighter Mk. I',
- type: 'ship',
- rarity: 'common',
- price: 5000,
- currency: 'credits',
- description: 'Fast and agile fighter ship',
- stats: { attack: 15, speed: 20, defense: 8 }
- },
- {
- id: 'cruiser_mk1',
- name: 'Cruiser Mk. I',
- type: 'ship',
- rarity: 'uncommon',
- price: 15000,
- currency: 'credits',
- description: 'Well-balanced cruiser for combat',
- stats: { attack: 20, speed: 10, defense: 15 }
- }
- ],
- upgrades: [
- {
- id: 'weapon_upgrade_1',
- name: 'Weapon Upgrade I',
- type: 'upgrade',
- rarity: 'common',
- price: 500,
- currency: 'credits',
- description: 'Increases weapon damage by 10%',
- effect: { attackMultiplier: 1.1 }
- },
- {
- id: 'shield_upgrade_1',
- name: 'Shield Upgrade I',
- type: 'upgrade',
- rarity: 'common',
- price: 400,
- currency: 'credits',
- description: 'Increases defense by 5 points',
- effect: { defense: 5 }
- }
- ],
- cosmetics: [
- {
- id: 'blue_paint',
- name: 'Blue Paint Job',
- type: 'cosmetic',
- rarity: 'common',
- price: 100,
- currency: 'gems',
- description: 'Custom blue paint for your ship'
- },
- {
- id: 'golden_trim',
- name: 'Golden Trim',
- type: 'cosmetic',
- rarity: 'rare',
- price: 500,
- currency: 'gems',
- description: 'Luxurious golden trim for your ship'
- }
- ],
- consumables: [
- {
- id: 'mega_health_kit',
- name: 'Mega Health Kit',
- type: 'consumable',
- rarity: 'uncommon',
- price: 50,
- currency: 'credits',
- description: 'Restores full health',
- effect: { heal: 999 }
- },
- {
- id: 'energy_boost',
- name: 'Energy Boost',
- type: 'consumable',
- rarity: 'common',
- price: 25,
- currency: 'credits',
- description: 'Restores 50 energy',
- effect: { energy: 50 }
- }
- ]
-};
-
-// Starter equipment for new players
-const STARTER_EQUIPMENT = {
- starter_blaster: {
- id: 'starter_blaster',
- name: 'Common Blaster',
- type: 'weapon',
- rarity: 'common',
- description: 'A reliable basic blaster for new pilots',
- stats: { attack: 5, criticalChance: 0.02 },
- equipable: true,
- slot: 'weapon',
- value: 100,
- stackable: false
- },
- basic_armor: {
- id: 'basic_armor',
- name: 'Basic Armor',
- type: 'armor',
- rarity: 'common',
- description: 'Standard issue armor for basic protection',
- stats: { defense: 3, health: 10 },
- equipable: true,
- slot: 'armor',
- value: 150,
- stackable: false
- }
-};
-
-// Achievement definitions
-const ACHIEVEMENTS = {
- first_victory: {
- name: 'First Victory',
- description: 'Win your first dungeon',
- requirement: { dungeonsCompleted: 1 },
- reward: { gems: 10, experience: 100 },
- icon: 'fa-trophy'
- },
- dungeon_master: {
- name: 'Dungeon Master',
- description: 'Complete 50 dungeons',
- requirement: { dungeonsCompleted: 50 },
- reward: { gems: 100, experience: 1000 },
- icon: 'fa-dungeon'
- },
- level_master: {
- name: 'Level Master',
- description: 'Reach level 50',
- requirement: { level: 50 },
- reward: { gems: 200, experience: 5000 },
- icon: 'fa-level-up-alt'
- },
- wealthy_commander: {
- name: 'Wealthy Commander',
- description: 'Accumulate 1,000,000 credits',
- requirement: { credits: 1000000 },
- reward: { gems: 150, experience: 2000 },
- icon: 'fa-coins'
- },
- skill_expert: {
- name: 'Skill Expert',
- description: 'Max out any skill',
- requirement: { maxSkillLevel: 10 },
- reward: { gems: 75, experience: 1500 },
- icon: 'fa-graduation-cap'
- }
-};
-
-// Game messages and notifications
-const GAME_MESSAGES = {
- welcome: 'Welcome to Galaxy Strike Online, Commander!',
- levelUp: 'Level Up! You are now level {level}!',
- questCompleted: 'Quest completed: {questName}!',
- dungeonCompleted: 'Dungeon completed! Time: {time}',
- achievementUnlocked: 'Achievement Unlocked: {achievementName}!',
- insufficientCredits: 'Not enough credits!',
- insufficientGems: 'Not enough gems!',
- insufficientEnergy: 'Not enough energy!',
- inventoryFull: 'Inventory is full!',
- skillPointsAvailable: 'You have {points} skill points available!',
- dailyReward: 'Daily reward claimed! Day {day}',
- offlineRewards: 'Welcome back! You were offline for {time}'
-};
-
-// Utility functions
-const GameUtils = {
- // Get random item from array
- getRandomItem(array) {
- return array[Math.floor(Math.random() * array.length)];
- },
-
- // Get random integer between min and max (inclusive)
- getRandomInt(min, max) {
- return Math.floor(Math.random() * (max - min + 1)) + min;
- },
-
- // Get random float between min and max
- getRandomFloat(min, max) {
- return Math.random() * (max - min) + min;
- },
-
- // Check if chance succeeds
- checkChance(chance) {
- return Math.random() < chance;
- },
-
- // Format large numbers with suffixes
- formatNumber(num) {
- if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
- if (num >= 1000) return (num / 1000).toFixed(1) + 'K';
- return Math.floor(num).toString();
- },
-
- // Format time in milliseconds to readable string
- formatTime(milliseconds) {
- const seconds = Math.floor(milliseconds / 1000);
- const minutes = Math.floor(seconds / 60);
- const hours = Math.floor(minutes / 60);
- const days = Math.floor(hours / 24);
-
- if (days > 0) return `${days}d ${hours % 24}h`;
- if (hours > 0) return `${hours}h ${minutes % 60}m`;
- if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
- return `${seconds}s`;
- },
-
- // Calculate experience needed for level
- getExperienceForLevel(level) {
- return EXPERIENCE_TABLE[level] || 0;
- },
-
- // Get item rarity by chance
- getItemRarity() {
- const roll = Math.random();
- let cumulative = 0;
-
- for (const [rarity, data] of Object.entries(ITEM_RARITIES)) {
- cumulative += data.dropChance;
- if (roll <= cumulative) {
- return rarity;
- }
- }
-
- return 'common';
- },
-
- // Deep clone object
- deepClone(obj) {
- return JSON.parse(JSON.stringify(obj));
- },
-
- // Generate unique ID
- generateId() {
- return Date.now().toString() + Math.random().toString(36).substr(2, 9);
- }
-};
-
-// Export for use in other modules
-if (typeof module !== 'undefined' && module.exports) {
- module.exports = {
- GAME_CONFIG,
- PLAYER_DEFAULTS,
- EXPERIENCE_TABLE,
- ITEM_RARITIES,
- ENEMY_TEMPLATES,
- DUNGEON_CONFIGS,
- SKILL_DEFINITIONS,
- SHOP_ITEMS,
- ACHIEVEMENTS,
- GAME_MESSAGES,
- GameUtils
- };
-}
diff --git a/Client-Server/js/systems/CraftingSystem.js b/Client-Server/js/systems/CraftingSystem.js
deleted file mode 100644
index a21618e..0000000
--- a/Client-Server/js/systems/CraftingSystem.js
+++ /dev/null
@@ -1,657 +0,0 @@
-/**
- * Galaxy Strike Online - Crafting System
- * Handles item crafting, recipes, and crafting skill progression
- */
-
-class CraftingSystem extends BaseSystem {
- constructor(gameEngine) {
- super(gameEngine);
-
- this.recipes = new Map();
- this.currentCategory = 'weapons';
- this.selectedRecipe = null;
-
- this.initializeRecipes();
- }
-
- initializeRecipes() {
- // Weapon Recipes
- this.addRecipe('basic_blaster', {
- name: 'Basic Blaster',
- category: 'weapons',
- description: 'A simple energy blaster for beginners',
- requirements: {
- weapon_crafting: 1,
- crafting: 1
- },
- materials: [
- { id: 'iron_ore', quantity: 5 },
- { id: 'energy_crystal', quantity: 2 }
- ],
- results: [
- { id: 'basic_blaster', quantity: 1 }
- ],
- experience: 10,
- craftingTime: 3000 // 3 seconds
- });
-
- this.addRecipe('enhanced_blaster', {
- name: 'Enhanced Blaster',
- category: 'weapons',
- description: 'An improved blaster with better damage output',
- requirements: {
- weapon_crafting: 3,
- crafting: 5
- },
- materials: [
- { id: 'iron_ore', quantity: 10 },
- { id: 'energy_crystal', quantity: 5 },
- { id: 'copper_wire', quantity: 3 }
- ],
- results: [
- { id: 'enhanced_blaster', quantity: 1 }
- ],
- experience: 25,
- craftingTime: 5000 // 5 seconds
- });
-
- this.addRecipe('laser_sniper_rifle', {
- name: 'Laser Sniper Rifle',
- category: 'weapons',
- description: 'A long-range precision laser weapon',
- requirements: {
- weapon_crafting: 3,
- crafting: 5
- },
- materials: [
- { id: 'advanced_circuitboard', quantity: 2 },
- { id: 'energy_crystal', quantity: 8 },
- { id: 'steel_plate', quantity: 5 },
- { id: 'copper_wire', quantity: 4 }
- ],
- results: [
- { id: 'laser_sniper_rifle', quantity: 1 }
- ],
- experience: 40,
- craftingTime: 8000 // 8 seconds
- });
-
- this.addRecipe('plasma_cannon', {
- name: 'Plasma Cannon',
- category: 'weapons',
- description: 'A devastating plasma-based weapon',
- requirements: {
- weapon_crafting: 5,
- crafting: 7
- },
- materials: [
- { id: 'advanced_components', quantity: 3 },
- { id: 'energy_crystal', quantity: 12 },
- { id: 'steel_plate', quantity: 8 },
- { id: 'battery', quantity: 3 }
- ],
- results: [
- { id: 'plasma_cannon', quantity: 1 }
- ],
- experience: 60,
- craftingTime: 12000 // 12 seconds
- });
-
- // Armor Recipes
- this.addRecipe('basic_armor', {
- name: 'Basic Armor',
- category: 'armor',
- description: 'Light armor providing basic protection',
- requirements: {
- armor_forging: 1,
- crafting: 1
- },
- materials: [
- { id: 'iron_ore', quantity: 8 },
- { id: 'leather', quantity: 3 }
- ],
- results: [
- { id: 'basic_armor', quantity: 1 }
- ],
- experience: 15,
- craftingTime: 4000 // 4 seconds
- });
-
- this.addRecipe('reinforced_armor', {
- name: 'Reinforced Armor',
- category: 'armor',
- description: 'Heavy armor with enhanced protection',
- requirements: {
- armor_forging: 4,
- crafting: 6
- },
- materials: [
- { id: 'iron_ore', quantity: 15 },
- { id: 'steel_plate', quantity: 5 },
- { id: 'leather', quantity: 5 }
- ],
- results: [
- { id: 'reinforced_armor', quantity: 1 }
- ],
- experience: 35,
- craftingTime: 6000 // 6 seconds
- });
-
- // Item Recipes
- this.addRecipe('health_kit', {
- name: 'Health Kit',
- category: 'items',
- description: 'A medical kit that restores health',
- requirements: {
- crafting: 2
- },
- materials: [
- { id: 'herbs', quantity: 3 },
- { id: 'bandages', quantity: 2 }
- ],
- results: [
- { id: 'health_kit', quantity: 3 }
- ],
- experience: 5,
- craftingTime: 2000 // 2 seconds
- });
-
- this.addRecipe('basic_circuit', {
- name: 'Basic Circuit',
- category: 'items',
- description: 'Create a basic electronic circuit',
- requirements: {
- crafting: 3
- },
- materials: [
- { id: 'basic_circuitboard', quantity: 1 },
- { id: 'copper_wire', quantity: 2 }
- ],
- results: [
- { id: 'basic_circuit', quantity: 1 }
- ],
- experience: 8,
- craftingTime: 2500 // 2.5 seconds
- });
-
- this.addRecipe('advanced_circuit', {
- name: 'Advanced Circuit',
- category: 'items',
- description: 'Create an advanced electronic circuit',
- requirements: {
- crafting: 5
- },
- materials: [
- { id: 'advanced_circuitboard', quantity: 1 },
- { id: 'energy_crystal', quantity: 2 },
- { id: 'copper_wire', quantity: 3 }
- ],
- results: [
- { id: 'advanced_circuit', quantity: 1 }
- ],
- experience: 15,
- craftingTime: 4000 // 4 seconds
- });
-
- this.addRecipe('electronic_device', {
- name: 'Electronic Device',
- category: 'items',
- description: 'Create a complex electronic device',
- requirements: {
- crafting: 7
- },
- materials: [
- { id: 'advanced_components', quantity: 1 },
- { id: 'battery', quantity: 2 },
- { id: 'common_circuitboard', quantity: 1 }
- ],
- results: [
- { id: 'electronic_device', quantity: 1 }
- ],
- experience: 20,
- craftingTime: 5000 // 5 seconds
- });
-
- // Ship Component Recipes
- this.addRecipe('shield_generator', {
- name: 'Shield Generator',
- category: 'ships',
- description: 'A basic shield generator for ship protection',
- requirements: {
- engineering: 2,
- crafting: 4
- },
- materials: [
- { id: 'energy_crystal', quantity: 8 },
- { id: 'steel_plate', quantity: 5 },
- { id: 'copper_wire', quantity: 4 }
- ],
- results: [
- { id: 'shield_generator', quantity: 1 }
- ],
- experience: 30,
- craftingTime: 8000 // 8 seconds
- });
-
- this.addRecipe('engine_upgrade', {
- name: 'Engine Upgrade',
- category: 'ships',
- description: 'An upgrade that improves ship engine performance',
- requirements: {
- engineering: 5,
- crafting: 7
- },
- materials: [
- { id: 'steel_plate', quantity: 10 },
- { id: 'energy_crystal', quantity: 6 },
- { id: 'rare_metal', quantity: 2 }
- ],
- results: [
- { id: 'engine_upgrade', quantity: 1 }
- ],
- experience: 50,
- craftingTime: 10000 // 10 seconds
- });
-
- this.addRecipe('quantum_computer', {
- name: 'Quantum Computer',
- category: 'ships',
- description: 'Advanced quantum computer for high-end ship systems',
- requirements: {
- engineering: 8,
- crafting: 10
- },
- materials: [
- { id: 'advanced_components', quantity: 3 },
- { id: 'energy_crystal', quantity: 8 },
- { id: 'rare_metal', quantity: 4 },
- { id: 'copper_wire', quantity: 6 }
- ],
- results: [
- { id: 'quantum_computer', quantity: 1 }
- ],
- experience: 100,
- craftingTime: 15000 // 15 seconds
- });
- }
-
- addRecipe(id, recipe) {
- recipe.id = id;
- recipe.unlocked = false;
- this.recipes.set(id, recipe);
- }
-
- update(deltaTime) {
- // Check for newly unlocked recipes
- this.checkRecipeUnlocks();
- }
-
- checkRecipeUnlocks() {
- const skillSystem = this.game.systems.skillSystem;
- if (!skillSystem) return;
-
- for (const [id, recipe] of this.recipes) {
- if (!recipe.unlocked) {
- let canCraft = true;
-
- // Check skill requirements
- if (recipe.requirements) {
- for (const [skillName, requiredLevel] of Object.entries(recipe.requirements)) {
- const skillLevel = skillSystem.getSkillLevel(skillName);
- if (skillLevel < requiredLevel) {
- canCraft = false;
- break;
- }
- }
- }
-
- if (canCraft) {
- recipe.unlocked = true;
- console.log(`[CRAFTING] Recipe unlocked: ${recipe.name}`);
- }
- }
- }
- }
-
- getRecipesByCategory(category) {
- return Array.from(this.recipes.values())
- .filter(recipe => recipe.category === category);
- }
-
- canCraftRecipe(recipeId) {
- const recipe = this.recipes.get(recipeId);
- if (!recipe) return false;
-
- // Check skill requirements
- if (recipe.requirements) {
- const skillSystem = this.game.systems.skillSystem;
- if (!skillSystem) return false;
-
- for (const [skillName, requiredLevel] of Object.entries(recipe.requirements)) {
- const skillLevel = skillSystem.getSkillLevel(skillName);
- if (skillLevel < requiredLevel) {
- return false;
- }
- }
- }
-
- // Check materials
- if (recipe.materials) {
- for (const material of recipe.materials) {
- const inventory = this.game.systems.inventory;
- if (!inventory || !inventory.hasItem(material.id, material.quantity)) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- getMissingMaterials(recipeId) {
- const recipe = this.recipes.get(recipeId);
- if (!recipe || !recipe.materials) return [];
-
- const missing = [];
- const inventory = this.game.systems.inventory;
-
- console.log(`[CRAFTING DEBUG] Checking materials for recipe: ${recipe.name}`);
- console.log(`[CRAFTING DEBUG] Inventory system:`, inventory);
-
- for (const material of recipe.materials) {
- let currentCount = 0;
-
- // Safely get current material count
- if (inventory && typeof inventory.getItemCount === 'function') {
- try {
- currentCount = inventory.getItemCount(material.id);
- // Ensure we have a valid number
- currentCount = typeof currentCount === 'number' && !isNaN(currentCount) ? currentCount : 0;
- } catch (error) {
- console.log(`[CRAFTING DEBUG] Error getting count for ${material.id}:`, error);
- currentCount = 0;
- }
- console.log(`[CRAFTING DEBUG] Material ${material.id}: current=${currentCount}, required=${material.quantity}`);
- } else {
- console.log(`[CRAFTING DEBUG] Inventory or getItemCount not available for ${material.id}`);
- currentCount = 0;
- }
-
- // Ensure required quantity is also a valid number
- const requiredQuantity = typeof material.quantity === 'number' && !isNaN(material.quantity) ? material.quantity : 0;
-
- // Check if we have enough materials
- if (currentCount < requiredQuantity) {
- missing.push({
- id: material.id,
- required: requiredQuantity,
- current: currentCount,
- missing: Math.max(0, requiredQuantity - currentCount)
- });
- }
- }
-
- console.log(`[CRAFTING DEBUG] Missing materials:`, missing);
- return missing;
- }
-
- async craftRecipe(recipeId) {
- const recipe = this.recipes.get(recipeId);
- if (!recipe) {
- console.error(`[CRAFTING] Recipe not found: ${recipeId}`);
- return false;
- }
-
- if (!this.canCraftRecipe(recipeId)) {
- console.log(`[CRAFTING] Cannot craft recipe: ${recipe.name}`);
- return false;
- }
-
- console.log(`[CRAFTING] Starting to craft: ${recipe.name}`);
-
- // Remove materials
- if (recipe.materials) {
- for (const material of recipe.materials) {
- this.game.systems.inventory.removeItem(material.id, material.quantity);
- }
- }
-
- // Add crafting experience
- if (recipe.experience) {
- this.game.systems.skillSystem.awardCraftingExperience(recipe.experience);
- }
-
- // Wait for crafting time
- await new Promise(resolve => setTimeout(resolve, recipe.craftingTime));
-
- // Add results to inventory
- if (recipe.results) {
- for (const result of recipe.results) {
- this.game.systems.inventory.addItem(result.id, result.quantity);
- }
- }
-
- // Update quest progress
- if (this.game.systems.questSystem) {
- this.game.systems.questSystem.onItemCrafted();
- }
-
- console.log(`[CRAFTING] Successfully crafted: ${recipe.name}`);
- return true;
- }
-
- selectRecipe(recipeId) {
- this.selectedRecipe = this.recipes.get(recipeId);
- return this.selectedRecipe;
- }
-
- getSelectedRecipe() {
- return this.selectedRecipe;
- }
-
- updateUI() {
- this.updateRecipeList();
- this.updateCraftingDetails();
- this.updateCraftingInfo();
- }
-
- updateRecipeList() {
- const recipeListElement = document.getElementById('recipeList');
- if (!recipeListElement) return;
-
- const recipes = this.getRecipesByCategory(this.currentCategory);
-
- recipeListElement.innerHTML = '';
-
- if (recipes.length === 0) {
- recipeListElement.innerHTML = 'No recipes available in this category
';
- return;
- }
-
- recipes.forEach(recipe => {
- const recipeElement = document.createElement('div');
- recipeElement.className = 'recipe-item';
- recipeElement.dataset.recipeId = recipe.id;
-
- const canCraft = this.canCraftRecipe(recipe.id);
- const missingMaterials = this.getMissingMaterials(recipe.id);
-
- // Check if recipe is unlocked (skill requirements met)
- const skillSystem = this.game.systems.skillSystem;
- let skillRequirementsMet = true;
- if (recipe.requirements && skillSystem) {
- for (const [skillName, requiredLevel] of Object.entries(recipe.requirements)) {
- const skillLevel = skillSystem.getSkillLevel(skillName);
- if (skillLevel < requiredLevel) {
- skillRequirementsMet = false;
- break;
- }
- }
- }
-
- // Apply styling based on status
- if (!skillRequirementsMet) {
- recipeElement.classList.add('locked');
- } else if (!canCraft) {
- recipeElement.classList.add('missing-materials');
- } else {
- recipeElement.classList.add('can-craft');
- }
-
- // Generate requirements text
- const requirementsText = recipe.requirements ?
- Object.entries(recipe.requirements).map(([skill, level]) => `${skill}: ${level}`).join(', ') : 'None';
-
- // Generate materials with missing status
- const materialsHtml = recipe.materials ? recipe.materials.map(mat => {
- const missing = missingMaterials.find(m => m.id === mat.id);
- const currentCount = missing ? missing.current : 0;
- const requiredCount = mat.quantity || 0;
-
- if (missing) {
- return `
- ${mat.id}
- ${currentCount}/${requiredCount}
-
`;
- } else {
- return `
- ${mat.id}
- ${currentCount}/${requiredCount}
-
`;
- }
- }).join('') : '';
-
- recipeElement.innerHTML = `
-
- ${recipe.description}
-
- ${materialsHtml}
-
- ${missingMaterials.length > 0 ? `
-
-
- Missing: ${missingMaterials.map(m => `${m.missing}x ${m.id}`).join(', ')}
-
- ` : ''}
-
-
- ${recipe.craftingTime / 1000}s
-
- `;
-
- recipeElement.addEventListener('click', () => {
- this.selectRecipe(recipe.id);
- this.updateCraftingDetails();
- });
-
- recipeListElement.appendChild(recipeElement);
- });
- }
-
- updateCraftingDetails() {
- const detailsElement = document.getElementById('craftingDetails');
- if (!detailsElement) return;
-
- if (!this.selectedRecipe) {
- detailsElement.innerHTML = `
-
-
Select a Recipe
-
Choose a recipe from the list to see details and craft items.
-
- `;
- return;
- }
-
- const recipe = this.selectedRecipe;
- const canCraft = this.canCraftRecipe(recipe.id);
-
- detailsElement.innerHTML = `
-
-
${recipe.name}
-
${recipe.description}
-
-
-
Requirements:
- ${recipe.requirements ? Object.entries(recipe.requirements).map(([skill, level]) =>
- `
- ${skill}
- Level ${level}
-
`
- ).join('') : '
No special requirements
'}
-
-
-
-
Materials Needed:
- ${recipe.materials ? recipe.materials.map(mat =>
- `
- ${mat.id}
- x${mat.quantity}
- Have: ${this.game.systems.inventory?.getItemCount(mat.id) || 0}
-
`
- ).join('') : '
No materials needed
'}
-
-
-
-
Results:
- ${recipe.results ? recipe.results.map(result =>
- `
- ${result.id}
- x${result.quantity}
-
`
- ).join('') : ''}
-
-
-
-
-
- ${recipe.experience} XP
-
-
-
- ${recipe.craftingTime / 1000} seconds
-
-
-
-
- ${canCraft ? 'Craft Item' : 'Cannot Craft'}
-
-
- `;
- }
-
- updateCraftingInfo() {
- const skillSystem = this.game.systems.skillSystem;
- if (!skillSystem) return;
-
- const craftingLevel = skillSystem.getSkillLevel('crafting');
- const craftingExp = skillSystem.getSkillExperience('crafting');
- const expNeeded = skillSystem.getExperienceNeeded('crafting');
-
- const levelElement = document.getElementById('craftingLevel');
- const expElement = document.getElementById('craftingExp');
-
- if (levelElement) levelElement.textContent = craftingLevel;
- if (expElement) expElement.textContent = `${craftingExp}/${expNeeded}`;
- }
-
- switchCategory(category) {
- this.currentCategory = category;
- this.selectedRecipe = null;
-
- // Update UI only if in multiplayer mode or game is actively running
- const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
-
- if (shouldUpdateUI) {
- this.updateUI();
- }
- }
-}
-
-// Export for use in GameEngine
-if (typeof module !== 'undefined' && module.exports) {
- module.exports = CraftingSystem;
-}
diff --git a/Client-Server/js/systems/DungeonSystem.js b/Client-Server/js/systems/DungeonSystem.js
deleted file mode 100644
index 851c485..0000000
--- a/Client-Server/js/systems/DungeonSystem.js
+++ /dev/null
@@ -1,1985 +0,0 @@
-/**
- * Galaxy Strike Online - Dungeon System
- * Manages procedural dungeon generation and exploration
- */
-
-class DungeonSystem {
- constructor(gameEngine) {
- this.game = gameEngine;
-
- // Dungeon configuration
- this.dungeonTypes = {
- tutorial: {
- name: 'Tutorial Dungeon',
- description: 'Learn the basics of dungeon exploration in this guided tutorial',
- difficulty: 'tutorial',
- enemyTypes: ['training_drone', 'practice_target'],
- rewardMultiplier: 0.5,
- oneTimeOnly: true,
- healthType: 'player', // Ground mission
- energyCost: 0
- },
- alien_ruins: {
- name: 'Alien Ruins',
- description: 'Ancient alien structures filled with mysterious technology',
- difficulty: 'medium',
- enemyTypes: ['alien_guardian', 'ancient_drone', 'crystal_golem'],
- rewardMultiplier: 1.2,
- healthType: 'player', // Ground mission
- energyCost: 20
- },
- pirate_lair: {
- name: 'Pirate Lair',
- description: 'Dangerous pirate hideouts with valuable loot',
- difficulty: 'easy',
- enemyTypes: ['space_pirate', 'pirate_captain', 'defense_turret'],
- rewardMultiplier: 1.0,
- healthType: 'ship', // Space mission
- energyCost: 15
- },
- corrupted_vault: {
- name: 'Corrupted AI Vault',
- description: ' malfunctioning AI facilities with corrupted security',
- difficulty: 'hard',
- enemyTypes: ['security_drone', 'corrupted_ai', 'virus_program'],
- rewardMultiplier: 1.5,
- healthType: 'ship', // Space mission
- energyCost: 25
- },
- asteroid_mine: {
- name: 'Asteroid Mine',
- description: 'Abandoned mining facilities in asteroid fields',
- difficulty: 'easy',
- enemyTypes: ['mining_drone', 'rock_creature', 'explosive_asteroid'],
- rewardMultiplier: 0.8,
- healthType: 'ship', // Space mission
- energyCost: 10
- },
- nebula_anomaly: {
- name: 'Nebula Anomaly',
- description: 'Strange energy anomalies in deep space',
- difficulty: 'extreme',
- enemyTypes: ['energy_being', 'phase_shifter', 'quantum_entity'],
- rewardMultiplier: 2.0,
- healthType: 'ship', // Space mission
- energyCost: 30
- },
-
- // NEW DUNGEONS - Space Theme
- space_station: {
- name: 'Abandoned Space Station',
- description: 'A derelict space station floating in the void',
- difficulty: 'medium',
- enemyTypes: ['maintenance_drone', 'security_android', 'station_ai'],
- rewardMultiplier: 1.3,
- healthType: 'player',
- energyCost: 22
- },
- comet_core: {
- name: 'Comet Core',
- description: 'The frozen heart of a passing comet',
- difficulty: 'hard',
- enemyTypes: ['ice_elemental', 'frost_wyrm', 'crystal_guardian'],
- rewardMultiplier: 1.6,
- healthType: 'ship',
- energyCost: 28
- },
- black_hole_perimeter: {
- name: 'Black Hole Perimeter',
- description: 'Dangerous space near a black hole event horizon',
- difficulty: 'extreme',
- enemyTypes: ['gravity_wraith', 'void_stalker', 'singularity_spawn'],
- rewardMultiplier: 2.5,
- healthType: 'ship',
- energyCost: 35
- },
- star_forge: {
- name: 'Star Forge',
- description: 'Ancient facility that harnesses stellar energy',
- difficulty: 'hard',
- enemyTypes: ['plasma_elemental', 'solar_guardian', 'fusion_core'],
- rewardMultiplier: 1.8,
- healthType: 'ship',
- energyCost: 30
- },
- debris_field: {
- name: 'Ship Debris Field',
- description: 'Graveyard of destroyed spacecraft',
- difficulty: 'easy',
- enemyTypes: ['scrap_golem', 'hull_breacher', 'salage_drone'],
- rewardMultiplier: 0.9,
- healthType: 'ship',
- energyCost: 12
- },
-
- // NEW DUNGEONS - Planet Theme
- jungle_temple: {
- name: 'Jungle Temple',
- description: 'Overgrown temple hidden in dense alien jungle',
- difficulty: 'medium',
- enemyTypes: ['plant_beast', 'tribal_warrior', 'jungle_spirit'],
- rewardMultiplier: 1.4,
- healthType: 'player',
- energyCost: 25
- },
- desert_pyramid: {
- name: 'Desert Pyramid',
- description: 'Ancient pyramid buried under endless sand dunes',
- difficulty: 'hard',
- enemyTypes: ['sand_worm', 'mummy_guardian', 'heat_elemental'],
- rewardMultiplier: 1.7,
- healthType: 'player',
- energyCost: 28
- },
- volcanic_caverns: {
- name: 'Volcanic Caverns',
- description: 'Molten caverns deep within an active volcano',
- difficulty: 'hard',
- enemyTypes: ['lava_elemental', 'fire_demon', 'magma_beast'],
- rewardMultiplier: 1.6,
- healthType: 'player',
- energyCost: 26
- },
- arctic_research: {
- name: 'Arctic Research Base',
- description: 'Frozen research facility with failed experiments',
- difficulty: 'medium',
- enemyTypes: ['cryo_mutant', 'frost_android', 'ice_wraith'],
- rewardMultiplier: 1.5,
- healthType: 'player',
- energyCost: 24
- },
- swamp_lair: {
- name: 'Swamp Lair',
- description: 'Murky swamp inhabited by strange creatures',
- difficulty: 'easy',
- enemyTypes: ['swamp_beast', 'toxic_spitter', 'mud_golem'],
- rewardMultiplier: 1.1,
- healthType: 'player',
- energyCost: 18
- },
-
- // NEW DUNGEONS - Technology Theme
- cyber_realm: {
- name: 'Cyber Realm',
- description: 'Virtual reality space corrupted by malware',
- difficulty: 'hard',
- enemyTypes: ['glitch_wraith', 'firewall_guardian', 'data_vampire'],
- rewardMultiplier: 1.9,
- healthType: 'player',
- energyCost: 32
- },
- robot_factory: {
- name: 'Robot Factory',
- description: 'Automated factory producing hostile machines',
- difficulty: 'medium',
- enemyTypes: ['assembly_drone', 'welder_bot', 'factory_overseer'],
- rewardMultiplier: 1.4,
- healthType: 'player',
- energyCost: 23
- },
- quantum_lab: {
- name: 'Quantum Laboratory',
- description: 'Research facility experimenting with quantum physics',
- difficulty: 'extreme',
- enemyTypes: ['quantum_phantom', 'particle_accelerator', 'reality_bender'],
- rewardMultiplier: 2.3,
- healthType: 'player',
- energyCost: 38
- },
- server_farm: {
- name: 'Server Farm',
- description: 'Massive data center with rogue security programs',
- difficulty: 'medium',
- enemyTypes: ['sentinel_program', 'data_corruptor', 'system_guardian'],
- rewardMultiplier: 1.3,
- healthType: 'player',
- energyCost: 21
- },
-
- // NEW DUNGEONS - Biome/Elemental Theme
- crystal_caves: {
- name: 'Crystal Caves',
- description: 'Caves filled with energy-infused crystals',
- difficulty: 'medium',
- enemyTypes: ['crystal_golem', 'shard_elemental', 'resonance_beast'],
- rewardMultiplier: 1.4,
- healthType: 'player',
- energyCost: 22
- },
- toxic_wastes: {
- name: 'Toxic Wastes',
- description: 'Polluted wasteland filled with mutated creatures',
- difficulty: 'hard',
- enemyTypes: ['mutant_horror', 'toxic_slime', 'radiation_beast'],
- rewardMultiplier: 1.7,
- healthType: 'player',
- energyCost: 27
- },
- shadow_realm: {
- name: 'Shadow Realm',
- description: 'Dark dimension inhabited by shadow creatures',
- difficulty: 'extreme',
- enemyTypes: ['shadow_demon', 'nightmare_stalker', 'void_walker'],
- rewardMultiplier: 2.2,
- healthType: 'player',
- energyCost: 36
- },
- time_anomaly: {
- name: 'Time Anomaly',
- description: 'Area where time flows unpredictably',
- difficulty: 'extreme',
- enemyTypes: ['temporal_paradox', 'future_soldier', 'past_guardian'],
- rewardMultiplier: 2.4,
- healthType: 'player',
- energyCost: 40
- },
-
- // NEW DUNGEONS - Military/War Theme
- war_zone: {
- name: 'Active War Zone',
- description: 'Battlefield with ongoing combat operations',
- difficulty: 'hard',
- enemyTypes: ['enemy_soldier', 'combat_drone', 'field_commander'],
- rewardMultiplier: 1.8,
- healthType: 'player',
- energyCost: 29
- },
- military_base: {
- name: 'Abandoned Military Base',
- description: 'Former military installation with automated defenses',
- difficulty: 'medium',
- enemyTypes: ['turret_system', 'combat_android', 'base_commander'],
- rewardMultiplier: 1.5,
- healthType: 'player',
- energyCost: 24
- },
- weapons_testing: {
- name: 'Weapons Testing Facility',
- description: 'Secret facility testing advanced weaponry',
- difficulty: 'hard',
- enemyTypes: ['weapon_drone', 'test_subject', 'chief_scientist'],
- rewardMultiplier: 1.9,
- healthType: 'player',
- energyCost: 31
- },
-
- // NEW DUNGEONS - Special/Unique Theme
- dream_scape: {
- name: 'Dream Scape',
- description: 'Surreal landscape shaped by collective dreams',
- difficulty: 'medium',
- enemyTypes: ['nightmare_creature', 'dream_guardian', 'subconscious_demon'],
- rewardMultiplier: 1.6,
- healthType: 'player',
- energyCost: 26
- },
- memory_palace: {
- name: 'Memory Palace',
- description: 'Mental realm storing forgotten memories',
- difficulty: 'hard',
- enemyTypes: ['memory_fragment', 'forgetfulness_demon', 'nostalgia_spirit'],
- rewardMultiplier: 1.7,
- healthType: 'player',
- energyCost: 28
- },
- dimension_rift: {
- name: 'Dimension Rift',
- description: 'Tear between dimensions with interdimensional invaders',
- difficulty: 'extreme',
- enemyTypes: ['rift_demon', 'dimensional_hunter', 'reality_tear'],
- rewardMultiplier: 2.6,
- healthType: 'player',
- energyCost: 42
- }
- };
-
- // Current dungeon state
- this.currentDungeon = null;
- this.currentRoom = null;
- this.dungeonProgress = 0;
- this.isExploring = false;
-
- // Dungeon templates
- this.roomTypes = {
- entrance: { name: 'Entrance', enemies: 0, rewards: false },
- corridor: { name: 'Corridor', enemies: 1, rewards: false },
- chamber: { name: 'Chamber', enemies: 2, rewards: true },
- treasure: { name: 'Treasure Room', enemies: 0, rewards: true },
- boss: { name: 'Boss Room', enemies: 1, rewards: true, isBoss: true },
- exit: { name: 'Exit', enemies: 0, rewards: false }
- };
-
- // Enemy templates
- this.enemyTemplates = {
- // Original enemies
- training_drone: {
- name: 'Training Drone',
- health: 10,
- attack: 5,
- defense: 2,
- experience: 5,
- credits: 3
- },
- practice_target: {
- name: 'Practice Target',
- health: 5,
- attack: 0,
- defense: 0,
- experience: 2,
- credits: 1
- },
- alien_guardian: {
- name: 'Alien Guardian',
- health: 50,
- attack: 8,
- defense: 5,
- experience: 25,
- credits: 15
- },
- ancient_drone: {
- name: 'Ancient Drone',
- health: 30,
- attack: 12,
- defense: 2,
- experience: 20,
- credits: 10
- },
- crystal_golem: {
- name: 'Crystal Golem',
- health: 80,
- attack: 6,
- defense: 10,
- experience: 35,
- credits: 25
- },
- space_pirate: {
- name: 'Space Pirate',
- health: 25,
- attack: 10,
- defense: 3,
- experience: 15,
- credits: 12
- },
- pirate_captain: {
- name: 'Pirate Captain',
- health: 40,
- attack: 15,
- defense: 6,
- experience: 30,
- credits: 20
- },
- defense_turret: {
- name: 'Defense Turret',
- health: 20,
- attack: 18,
- defense: 8,
- experience: 18,
- credits: 8
- },
- security_drone: {
- name: 'Security Drone',
- health: 35,
- attack: 14,
- defense: 4,
- experience: 22,
- credits: 15
- },
- corrupted_ai: {
- name: 'Corrupted AI',
- health: 60,
- attack: 20,
- defense: 2,
- experience: 40,
- credits: 30
- },
- virus_program: {
- name: 'Virus Program',
- health: 15,
- attack: 25,
- defense: 1,
- experience: 20,
- credits: 12
- },
- mining_drone: {
- name: 'Mining Drone',
- health: 20,
- attack: 8,
- defense: 3,
- experience: 12,
- credits: 8
- },
- rock_creature: {
- name: 'Rock Creature',
- health: 45,
- attack: 6,
- defense: 12,
- experience: 25,
- credits: 15
- },
- explosive_asteroid: {
- name: 'Explosive Asteroid',
- health: 10,
- attack: 30,
- defense: 0,
- experience: 15,
- credits: 5
- },
- energy_being: {
- name: 'Energy Being',
- health: 55,
- attack: 22,
- defense: 3,
- experience: 45,
- credits: 35
- },
- phase_shifter: {
- name: 'Phase Shifter',
- health: 30,
- attack: 28,
- defense: 1,
- experience: 35,
- credits: 25
- },
- quantum_entity: {
- name: 'Quantum Entity',
- health: 70,
- attack: 35,
- defense: 5,
- experience: 60,
- credits: 50
- },
-
- // NEW ENEMIES - Space Theme
- maintenance_drone: {
- name: 'Maintenance Drone',
- health: 28,
- attack: 11,
- defense: 4,
- experience: 18,
- credits: 12
- },
- security_android: {
- name: 'Security Android',
- health: 42,
- attack: 16,
- defense: 7,
- experience: 28,
- credits: 20
- },
- station_ai: {
- name: 'Station AI',
- health: 65,
- attack: 24,
- defense: 3,
- experience: 45,
- credits: 32
- },
- ice_elemental: {
- name: 'Ice Elemental',
- health: 38,
- attack: 18,
- defense: 8,
- experience: 32,
- credits: 24
- },
- frost_wyrm: {
- name: 'Frost Wyrm',
- health: 72,
- attack: 26,
- defense: 6,
- experience: 52,
- credits: 38
- },
- gravity_wraith: {
- name: 'Gravity Wraith',
- health: 85,
- attack: 32,
- defense: 4,
- experience: 68,
- credits: 48
- },
- void_stalker: {
- name: 'Void Stalker',
- health: 78,
- attack: 38,
- defense: 5,
- experience: 72,
- credits: 52
- },
- singularity_spawn: {
- name: 'Singularity Spawn',
- health: 95,
- attack: 42,
- defense: 8,
- experience: 85,
- credits: 65
- },
- plasma_elemental: {
- name: 'Plasma Elemental',
- health: 58,
- attack: 28,
- defense: 4,
- experience: 48,
- credits: 35
- },
- solar_guardian: {
- name: 'Solar Guardian',
- health: 82,
- attack: 34,
- defense: 7,
- experience: 65,
- credits: 48
- },
- fusion_core: {
- name: 'Fusion Core',
- health: 68,
- attack: 30,
- defense: 12,
- experience: 58,
- credits: 42
- },
- scrap_golem: {
- name: 'Scrap Golem',
- health: 35,
- attack: 14,
- defense: 9,
- experience: 22,
- credits: 16
- },
- hull_breacher: {
- name: 'Hull Breacher',
- health: 32,
- attack: 20,
- defense: 3,
- experience: 26,
- credits: 18
- },
- salage_drone: {
- name: 'Salvage Drone',
- health: 22,
- attack: 12,
- defense: 5,
- experience: 16,
- credits: 11
- },
-
- // NEW ENEMIES - Planet Theme
- plant_beast: {
- name: 'Plant Beast',
- health: 48,
- attack: 15,
- defense: 8,
- experience: 35,
- credits: 26
- },
- tribal_warrior: {
- name: 'Tribal Warrior',
- health: 38,
- attack: 18,
- defense: 6,
- experience: 28,
- credits: 20
- },
- jungle_spirit: {
- name: 'Jungle Spirit',
- health: 55,
- attack: 22,
- defense: 4,
- experience: 42,
- credits: 30
- },
- sand_worm: {
- name: 'Sand Worm',
- health: 75,
- attack: 28,
- defense: 9,
- experience: 58,
- credits: 42
- },
- mummy_guardian: {
- name: 'Mummy Guardian',
- health: 62,
- attack: 24,
- defense: 7,
- experience: 48,
- credits: 35
- },
- heat_elemental: {
- name: 'Heat Elemental',
- health: 52,
- attack: 26,
- defense: 3,
- experience: 45,
- credits: 32
- },
- lava_elemental: {
- name: 'Lava Elemental',
- health: 68,
- attack: 30,
- defense: 5,
- experience: 55,
- credits: 40
- },
- fire_demon: {
- name: 'Fire Demon',
- health: 72,
- attack: 32,
- defense: 6,
- experience: 62,
- credits: 45
- },
- magma_beast: {
- name: 'Magma Beast',
- health: 85,
- attack: 28,
- defense: 12,
- experience: 68,
- credits: 50
- },
- cryo_mutant: {
- name: 'Cryo Mutant',
- health: 45,
- attack: 20,
- defense: 7,
- experience: 38,
- credits: 28
- },
- frost_android: {
- name: 'Frost Android',
- health: 52,
- attack: 22,
- defense: 8,
- experience: 42,
- credits: 30
- },
- ice_wraith: {
- name: 'Ice Wraith',
- health: 58,
- attack: 25,
- defense: 4,
- experience: 48,
- credits: 35
- },
- swamp_beast: {
- name: 'Swamp Beast',
- health: 35,
- attack: 16,
- defense: 9,
- experience: 24,
- credits: 18
- },
- toxic_spitter: {
- name: 'Toxic Spitter',
- health: 28,
- attack: 19,
- defense: 3,
- experience: 22,
- credits: 16
- },
- mud_golem: {
- name: 'Mud Golem',
- health: 42,
- attack: 12,
- defense: 11,
- experience: 26,
- credits: 19
- },
-
- // NEW ENEMIES - Technology Theme
- glitch_wraith: {
- name: 'Glitch Wraith',
- health: 48,
- attack: 26,
- defense: 2,
- experience: 45,
- credits: 32
- },
- firewall_guardian: {
- name: 'Firewall Guardian',
- health: 65,
- attack: 22,
- defense: 8,
- experience: 52,
- credits: 38
- },
- data_vampire: {
- name: 'Data Vampire',
- health: 38,
- attack: 30,
- defense: 3,
- experience: 35,
- credits: 26
- },
- assembly_drone: {
- name: 'Assembly Drone',
- health: 32,
- attack: 15,
- defense: 6,
- experience: 24,
- credits: 17
- },
- welder_bot: {
- name: 'Welder Bot',
- health: 28,
- attack: 20,
- defense: 4,
- experience: 22,
- credits: 15
- },
- factory_overseer: {
- name: 'Factory Overseer',
- health: 58,
- attack: 24,
- defense: 7,
- experience: 46,
- credits: 33
- },
- quantum_phantom: {
- name: 'Quantum Phantom',
- health: 78,
- attack: 35,
- defense: 4,
- experience: 68,
- credits: 50
- },
- particle_accelerator: {
- name: 'Particle Accelerator',
- health: 92,
- attack: 40,
- defense: 6,
- experience: 85,
- credits: 62
- },
- reality_bender: {
- name: 'Reality Bender',
- health: 88,
- attack: 45,
- defense: 3,
- experience: 92,
- credits: 68
- },
- sentinel_program: {
- name: 'Sentinel Program',
- health: 42,
- attack: 21,
- defense: 8,
- experience: 32,
- credits: 24
- },
- data_corruptor: {
- name: 'Data Corruptor',
- health: 35,
- attack: 25,
- defense: 3,
- experience: 28,
- credits: 20
- },
- system_guardian: {
- name: 'System Guardian',
- health: 55,
- attack: 23,
- defense: 9,
- experience: 42,
- credits: 30
- },
-
- // NEW ENEMIES - Biome/Elemental Theme
- shard_elemental: {
- name: 'Shard Elemental',
- health: 45,
- attack: 19,
- defense: 10,
- experience: 38,
- credits: 28
- },
- resonance_beast: {
- name: 'Resonance Beast',
- health: 52,
- attack: 22,
- defense: 6,
- experience: 42,
- credits: 30
- },
- mutant_horror: {
- name: 'Mutant Horror',
- health: 68,
- attack: 28,
- defense: 5,
- experience: 58,
- credits: 42
- },
- toxic_slime: {
- name: 'Toxic Slime',
- health: 42,
- attack: 18,
- defense: 8,
- experience: 32,
- credits: 24
- },
- radiation_beast: {
- name: 'Radiation Beast',
- health: 75,
- attack: 30,
- defense: 4,
- experience: 65,
- credits: 48
- },
- shadow_demon: {
- name: 'Shadow Demon',
- health: 72,
- attack: 34,
- defense: 3,
- experience: 68,
- credits: 50
- },
- nightmare_stalker: {
- name: 'Nightmare Stalker',
- health: 85,
- attack: 38,
- defense: 5,
- experience: 78,
- credits: 58
- },
- void_walker: {
- name: 'Void Walker',
- health: 92,
- attack: 42,
- defense: 7,
- experience: 88,
- credits: 65
- },
- temporal_paradox: {
- name: 'Temporal Paradox',
- health: 78,
- attack: 40,
- defense: 4,
- experience: 75,
- credits: 55
- },
- future_soldier: {
- name: 'Future Soldier',
- health: 65,
- attack: 32,
- defense: 9,
- experience: 58,
- credits: 42
- },
- past_guardian: {
- name: 'Past Guardian',
- health: 70,
- attack: 28,
- defense: 12,
- experience: 62,
- credits: 45
- },
-
- // NEW ENEMIES - Military/War Theme
- enemy_soldier: {
- name: 'Enemy Soldier',
- health: 45,
- attack: 20,
- defense: 7,
- experience: 35,
- credits: 26
- },
- combat_drone: {
- name: 'Combat Drone',
- health: 38,
- attack: 22,
- defense: 5,
- experience: 30,
- credits: 22
- },
- field_commander: {
- name: 'Field Commander',
- health: 62,
- attack: 28,
- defense: 9,
- experience: 52,
- credits: 38
- },
- turret_system: {
- name: 'Turret System',
- health: 48,
- attack: 26,
- defense: 10,
- experience: 38,
- credits: 28
- },
- combat_android: {
- name: 'Combat Android',
- health: 55,
- attack: 24,
- defense: 8,
- experience: 45,
- credits: 33
- },
- base_commander: {
- name: 'Base Commander',
- health: 72,
- attack: 30,
- defense: 11,
- experience: 62,
- credits: 45
- },
- weapon_drone: {
- name: 'Weapon Drone',
- health: 42,
- attack: 28,
- defense: 4,
- experience: 38,
- credits: 28
- },
- test_subject: {
- name: 'Test Subject',
- health: 58,
- attack: 25,
- defense: 6,
- experience: 48,
- credits: 35
- },
- chief_scientist: {
- name: 'Chief Scientist',
- health: 35,
- attack: 32,
- defense: 3,
- experience: 42,
- credits: 30
- },
-
- // NEW ENEMIES - Special/Unique Theme
- nightmare_creature: {
- name: 'Nightmare Creature',
- health: 62,
- attack: 28,
- defense: 5,
- experience: 55,
- credits: 40
- },
- dream_guardian: {
- name: 'Dream Guardian',
- health: 68,
- attack: 30,
- defense: 8,
- experience: 58,
- credits: 42
- },
- subconscious_demon: {
- name: 'Subconscious Demon',
- health: 75,
- attack: 34,
- defense: 4,
- experience: 68,
- credits: 50
- },
- memory_fragment: {
- name: 'Memory Fragment',
- health: 48,
- attack: 26,
- defense: 6,
- experience: 45,
- credits: 33
- },
- forgetfulness_demon: {
- name: 'Forgetfulness Demon',
- health: 55,
- attack: 30,
- defense: 3,
- experience: 48,
- credits: 35
- },
- nostalgia_spirit: {
- name: 'Nostalgia Spirit',
- health: 52,
- attack: 24,
- defense: 9,
- experience: 42,
- credits: 30
- },
- rift_demon: {
- name: 'Rift Demon',
- health: 88,
- attack: 44,
- defense: 5,
- experience: 92,
- credits: 68
- },
- dimensional_hunter: {
- name: 'Dimensional Hunter',
- health: 95,
- attack: 48,
- defense: 8,
- experience: 105,
- credits: 78
- },
- reality_tear: {
- name: 'Reality Tear',
- health: 102,
- attack: 52,
- defense: 3,
- experience: 115,
- credits: 85
- }
- };
-
- // Statistics
- this.stats = {
- dungeonsAttempted: 0,
- dungeonsCompleted: 0,
- totalEnemiesDefeated: 0,
- bestTime: Infinity,
- totalLootEarned: 0
- };
- }
-
- async initialize() {
- this.generateDungeonList();
- }
-
- generateDungeonList() {
-
- const dungeonListElement = document.getElementById('dungeonList');
- if (!dungeonListElement) {
- console.error('Dungeon list element not found!');
- return;
- }
-
- // Clear existing list
- dungeonListElement.innerHTML = '';
-
- const questSystem = this.game.systems.questSystem;
- const firstStepsQuest = questSystem ? questSystem.findQuest('tutorial_complete') : null;
- const showTutorialDungeon = firstStepsQuest && firstStepsQuest.status === 'active';
-
- Object.entries(this.dungeonTypes).forEach(([key, dungeon]) => {
- // Skip tutorial dungeon unless First Steps quest is active
- if (key === 'tutorial' && !showTutorialDungeon) {
- return;
- }
-
- const dungeonElement = document.createElement('div');
- dungeonElement.className = 'dungeon-item';
- dungeonElement.dataset.dungeonType = key;
-
- // Check if tutorial dungeon is completed
- const isCompleted = key === 'tutorial' && this.game.systems.player.stats.tutorialDungeonCompleted;
- const statusClass = isCompleted ? 'completed' : '';
- const statusText = isCompleted ? 'COMPLETED' : '';
-
- dungeonElement.innerHTML = `
- ${dungeon.name}
- ${dungeon.difficulty.toUpperCase()}
- ${statusText ? `${statusText}
` : ''}
- ${dungeon.description}
- Rewards: ${dungeon.rewardMultiplier}x
- Energy Cost: ${dungeon.energyCost || this.getEnergyCost(key)}
- `;
-
- dungeonElement.addEventListener('click', () => {
- if (isCompleted) {
- this.game.showNotification('Tutorial dungeon has already been completed!', 'warning', 3000);
- } else {
- this.selectDungeon(key);
- }
- });
-
- dungeonListElement.appendChild(dungeonElement);
- });
-
- }
-
- selectDungeon(type) {
-
- // Check energy cost
- const energyCost = this.getEnergyCost(type);
- const player = this.game.systems.player;
-
-
- if (!player.useEnergy(energyCost)) {
- this.game.showNotification(`Not enough energy! Need ${energyCost} energy`, 'error', 3000);
- return;
- }
-
-
- // Ensure we're on the Dungeons tab so the user can see the dungeon
- if (this.game.ui && this.game.ui.currentTab !== 'dungeons') {
- this.game.ui.switchTab('dungeons');
- }
-
- // Remove previous selection
- document.querySelectorAll('.dungeon-item').forEach(item => {
- item.classList.remove('selected');
- });
-
- // Add selection to clicked dungeon
- const selectedElement = document.querySelector(`[data-dungeon-type="${type}"]`);
- if (selectedElement) {
- selectedElement.classList.add('selected');
- }
-
-
- // Generate dungeon
- this.generateDungeon(type);
- this.displayDungeon();
-
-
- this.game.showNotification(`Entered dungeon! -${energyCost} energy`, 'info', 3000);
- }
-
- getEnergyCost(type) {
- const costs = {
- tutorial: 0,
- alien_ruins: 20,
- pirate_lair: 15,
- corrupted_vault: 25,
- asteroid_mine: 10,
- nebula_anomaly: 30
- };
- return costs[type] || 15;
- }
-
- calculateDungeonRewards(type, difficulty) {
- const dungeonTemplate = this.dungeonTypes[type];
- const baseRewards = {
- credits: 50,
- experience: 25,
- items: []
- };
-
- // Apply difficulty multiplier
- const difficultyMultipliers = {
- tutorial: 0.5,
- easy: 1.0,
- medium: 1.5,
- hard: 2.0
- };
-
- const multiplier = difficultyMultipliers[difficulty] || 1.0;
- const rewardMultiplier = dungeonTemplate.rewardMultiplier || 1.0;
-
- baseRewards.credits = Math.floor(baseRewards.credits * multiplier * rewardMultiplier);
- baseRewards.experience = Math.floor(baseRewards.experience * multiplier * rewardMultiplier);
-
- return baseRewards;
- }
-
- generateDungeon(type) {
-
- const dungeonTemplate = this.dungeonTypes[type];
-
- // Check if tutorial dungeon has already been completed
- if (type === 'tutorial' && this.game.systems.player.stats.tutorialDungeonCompleted) {
- this.game.showNotification('Tutorial dungeon has already been completed!', 'warning', 3000);
- return;
- }
-
-
- this.currentDungeon = {
- type: type,
- name: dungeonTemplate.name,
- description: dungeonTemplate.description,
- difficulty: dungeonTemplate.difficulty,
- healthType: dungeonTemplate.healthType,
- enemyTypes: dungeonTemplate.enemyTypes,
- rewardMultiplier: dungeonTemplate.rewardMultiplier,
- rooms: [],
- currentRoomIndex: 0,
- startTime: Date.now(),
- completed: false
- };
-
-
- // Generate rooms - ensure minimum of 3 rooms (entrance, at least 1 middle, boss)
- const roomCount = Math.max(3, this.game.getRandomInt(5, 8));
- this.currentDungeon.rooms = this.generateRoomLayout(roomCount, dungeonTemplate);
-
- // Set current room
- this.currentRoom = this.currentDungeon.rooms[0];
- this.dungeonProgress = 0;
-
-
- // Show dungeon view and hide list
- this.showDungeonView();
-
- }
-
- generateRoomLayout(roomCount, dungeonTemplate) {
- const rooms = [];
-
-
- // Always start with entrance
- rooms.push(this.createRoom('entrance', dungeonTemplate));
-
- // Generate middle rooms (subtract 2 for entrance and boss room)
- const middleRoomCount = roomCount - 2;
-
- for (let i = 0; i < middleRoomCount; i++) {
- const roomType = this.getRandomRoomType();
- const room = this.createRoom(roomType, dungeonTemplate);
- rooms.push(room);
- }
-
- // Always end with boss room (exit is handled by completing the boss room)
- rooms.push(this.createRoom('boss', dungeonTemplate));
-
-
- // Verify room accessibility
- for (let i = 0; i < rooms.length; i++) {
- }
-
- return rooms;
- }
-
- getRandomRoomType() {
- const weights = {
- corridor: 40,
- chamber: 30,
- treasure: 0, // Temporarily disabled
- entrance: 5,
- boss: 5,
- exit: 0
- };
-
- const totalWeight = Object.values(weights).reduce((sum, weight) => sum + weight, 0);
- let random = Math.random() * totalWeight;
-
- for (const [type, weight] of Object.entries(weights)) {
- random -= weight;
- if (random <= 0) {
- return type;
- }
- }
-
- return 'corridor';
- }
-
- createRoom(roomType, dungeonTemplate) {
- const template = this.roomTypes[roomType];
- const room = {
- type: roomType,
- name: template.name,
- enemies: [],
- rewards: null,
- explored: false,
- completed: false
- };
-
- // Generate enemies
- if (template.enemies > 0) {
- for (let i = 0; i < template.enemies; i++) {
- const enemyType = dungeonTemplate.enemyTypes[
- Math.floor(Math.random() * dungeonTemplate.enemyTypes.length)
- ];
- room.enemies.push(this.createEnemy(enemyType, template.isBoss));
- }
- }
-
- // Generate rewards
- if (template.rewards) {
- room.rewards = this.generateRoomRewards(dungeonTemplate.difficulty, template.isBoss);
- }
-
- return room;
- }
-
- createEnemy(enemyType, isBoss = false) {
- const template = this.enemyTemplates[enemyType];
- const bossMultiplier = isBoss ? 2.5 : 1.0;
-
- return {
- id: Date.now() + Math.random().toString(36).substr(2, 9),
- type: enemyType,
- name: isBoss ? `Boss ${template.name}` : template.name,
- health: Math.floor(template.health * bossMultiplier),
- maxHealth: Math.floor(template.health * bossMultiplier),
- attack: Math.floor(template.attack * bossMultiplier),
- defense: Math.floor(template.defense * bossMultiplier),
- experience: Math.floor(template.experience * bossMultiplier),
- credits: Math.floor(template.credits * bossMultiplier),
- isBoss: isBoss
- };
- }
-
- generateRoomRewards(difficulty, isBoss = false) {
- const baseRewards = this.game.systems.economy.generateRewards(difficulty, 'dungeon');
- const multiplier = isBoss ? 2.0 : 1.0;
-
- const rewards = {
- credits: Math.floor(baseRewards.credits * multiplier),
- experience: Math.floor(baseRewards.experience * multiplier),
- materials: []
- };
-
- // Add crafting materials based on chance and difficulty
- const materialChance = isBoss ? 0.9 : 0.4;
- if (Math.random() < materialChance) {
- const materialCount = isBoss ? this.game.getRandomInt(3, 6) : this.game.getRandomInt(1, 3);
-
- // Define material pools with weights for better balance
- const materialPools = {
- tutorial: [
- { id: 'iron_ore', weight: 40 },
- { id: 'copper_wire', weight: 30 },
- { id: 'herbs', weight: 20 },
- { id: 'bandages', weight: 10 }
- ],
- easy: [
- { id: 'iron_ore', weight: 30 },
- { id: 'copper_wire', weight: 25 },
- { id: 'energy_crystal', weight: 15 },
- { id: 'leather', weight: 15 },
- { id: 'herbs', weight: 10 },
- { id: 'bandages', weight: 5 }
- ],
- medium: [
- { id: 'iron_ore', weight: 25 },
- { id: 'steel_plate', weight: 20 },
- { id: 'energy_crystal', weight: 20 },
- { id: 'copper_wire', weight: 15 },
- { id: 'rare_metal', weight: 5 },
- { id: 'bandages', weight: 15 }
- ],
- hard: [
- { id: 'steel_plate', weight: 30 },
- { id: 'energy_crystal', weight: 25 },
- { id: 'rare_metal', weight: 15 },
- { id: 'battery', weight: 20 },
- { id: 'bandages', weight: 10 }
- ],
- extreme: [
- { id: 'rare_metal', weight: 30 },
- { id: 'energy_crystal', weight: 25 },
- { id: 'battery', weight: 25 },
- { id: 'advanced_components', weight: 20 }
- ]
- };
-
- const pool = materialPools[difficulty] || materialPools.easy;
-
- // Helper function to get weighted random material
- function getWeightedRandomMaterial(materialPool) {
- const totalWeight = materialPool.reduce((sum, mat) => sum + mat.weight, 0);
- let random = Math.random() * totalWeight;
-
- for (const material of materialPool) {
- random -= material.weight;
- if (random <= 0) {
- return material.id;
- }
- }
- return materialPool[0].id; // Fallback
- }
-
- for (let i = 0; i < materialCount; i++) {
- const material = getWeightedRandomMaterial(pool);
- const quantity = this.game.getRandomInt(1, isBoss ? 3 : 2);
-
- rewards.materials.push({
- id: material,
- quantity: quantity
- });
- }
- }
-
- return rewards;
- }
-
- displayDungeon() {
-
- const dungeonViewElement = document.getElementById('dungeonView');
-
- if (!dungeonViewElement) {
- const allElements = document.querySelectorAll('[id*="dungeon"]');
- return;
- }
-
- if (!this.currentDungeon) {
- return;
- }
-
- const room = this.currentRoom;
- if (room) {
- // Room exists, continue processing
- }
-
- const progress = Math.floor((this.currentDungeon.currentRoomIndex / this.currentDungeon.rooms.length) * 100);
-
-
- let content = `
-
-
-
-
- ${this.getHealthDisplay()}
-
-
-
-
${room.name}
- ${this.getRoomDescription(room)}
-
-
-
- ${this.getRoomContent(room)}
-
-
-
- ${this.getRoomActions(room)}
-
-
- `;
-
-
- dungeonViewElement.innerHTML = content;
-
-
- // Only add event listeners for buttons that don't use inline onclick handlers
- const completeDungeonBtn = dungeonViewElement.querySelector('button[onclick*="completeDungeon"]');
- if (completeDungeonBtn) {
- completeDungeonBtn.addEventListener('click', (e) => {
- e.preventDefault();
- this.completeDungeon();
- });
- }
-
- }
-
- getHealthDisplay() {
- const player = this.game.systems.player;
- const healthType = this.currentDungeon.healthType;
-
-
- if (healthType === 'ship') {
- const shipHealth = player.ship.health || 0;
- const shipMaxHealth = player.ship.maxHealth || 1000;
- const healthPercent = Math.round((shipHealth / shipMaxHealth) * 100);
- return `
-
-
Ship Health
-
-
${shipHealth} / ${shipMaxHealth}
-
- `;
- } else {
- const playerHealth = player.attributes.health || 0;
- const playerMaxHealth = player.attributes.maxHealth || 100;
- const healthPercent = Math.round((playerHealth / playerMaxHealth) * 100);
- return `
-
-
Player Health
-
-
${playerHealth} / ${playerMaxHealth}
-
- `;
- }
- }
-
- getRoomDescription(room) {
- const descriptions = {
- entrance: 'You enter the dungeon. The air is thick with anticipation.',
- corridor: 'A narrow corridor stretches ahead. You can hear distant sounds.',
- chamber: 'You enter a large chamber. The echoes of past battles linger here.',
- treasure: 'Golden light glimmers from piles of treasure in this room.',
- boss: 'A massive presence fills the room. This is the guardian of this dungeon.',
- exit: 'You can see the exit. Freedom awaits!'
- };
-
- return `${descriptions[room.type] || 'You continue exploring...'}
`;
- }
-
- getRoomContent(room) {
- if (room.completed) {
- return 'This room has been cleared.
';
- }
-
- if (room.enemies.length > 0) {
- const enemiesHtml = room.enemies.map(enemy => `
-
-
${enemy.name}
-
-
-
${enemy.health} / ${enemy.maxHealth}
-
-
ATK: ${enemy.attack} | DEF: ${enemy.defense}
-
- `).join('');
-
- return `${enemiesHtml}
`;
- }
-
- if (room.rewards && !room.completed) {
- return '';
- }
-
- return 'The room is empty and quiet.
';
- }
-
- getRoomActions(room) {
-
- if (room.completed) {
- if (this.currentDungeon.currentRoomIndex < this.currentDungeon.rooms.length - 1) {
- return 'Continue to Next Room ';
- } else {
- return 'Complete Dungeon ';
- }
- }
-
- if (room.enemies.length > 0) {
- const buttonHtml = 'Start Combat ';
- return buttonHtml;
- }
-
- if (room.rewards) {
- return 'Claim Rewards ';
- }
-
- // Check if this is the final room
- if (this.currentDungeon.currentRoomIndex >= this.currentDungeon.rooms.length - 1) {
- return 'Complete Dungeon ';
- }
-
- return 'Move Forward ';
- }
- async startCombat() {
-
- if (this.isExploring) {
- return;
- }
-
- this.isExploring = true;
- const room = this.currentRoom;
- const enemies = room.enemies.filter(e => e.health > 0);
-
-
- if (enemies.length === 0) {
- this.completeRoom();
- return;
- }
-
- // Simulate combat
- await this.simulateCombat(enemies);
- }
-
- async simulateCombat(enemies) {
-
- try {
- const player = this.game.systems.player;
-
- const healthType = this.currentDungeon.healthType;
-
- let combatLog = [];
-
- // Handle case where there are no enemies
- if (enemies.length === 0) {
- combatLog.push('Room was empty - no enemies found');
- this.completeRoom();
- return;
- }
-
- for (const [index, enemy] of enemies.entries()) {
-
- // Player attacks
- const playerDamage = player.calculateDamage(this.currentDungeon.difficulty);
-
- const actualDamage = Math.max(1, playerDamage.damage - enemy.defense);
- enemy.health = Math.max(0, enemy.health - actualDamage);
-
- combatLog.push(`You dealt ${actualDamage} damage to ${enemy.name}${playerDamage.isCritical ? ' (CRITICAL!)' : ''}`);
-
- // Update UI after player attack to show enemy health change
- this.displayDungeon();
- // Also update global player UI to ensure health bars are updated only if in multiplayer mode or game is actively running
- const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
-
- if (shouldUpdateUI && player.updateUI) {
- player.updateUI();
- }
-
- // Small delay to make the combat visible
- await new Promise(resolve => setTimeout(resolve, 500));
-
- // Enemy attacks if still alive
- if (enemy.health > 0) {
- let enemyDamage, damageTarget;
-
- if (healthType === 'ship') {
- // Space missions: higher damage, target ship health
- enemyDamage = Math.max(1, Math.floor(enemy.attack * 1.5) - player.ship.defense);
- damageTarget = 'ship';
- this.applyShipDamage(enemyDamage);
- combatLog.push(`${enemy.name} dealt ${enemyDamage} damage to your ship`);
- } else {
- // Ground missions: normal damage, target player health
- enemyDamage = Math.max(1, enemy.attack - player.attributes.defense);
- damageTarget = 'player';
- player.takeDamage(enemyDamage);
- combatLog.push(`${enemy.name} dealt ${enemyDamage} damage to you`);
- }
-
- // Update UI after enemy attack to show player health change
- this.displayDungeon();
- // Also update global player UI to ensure health bars are updated
- if (player.updateUI) {
- player.updateUI();
- }
-
- // Small delay to make the combat visible
- await new Promise(resolve => setTimeout(resolve, 500));
-
- // Check for destruction
- if (healthType === 'ship' && player.ship.health <= 0) {
- this.handleShipDestruction();
- } else if (healthType === 'player' && player.attributes.health <= 0) {
- this.handlePlayerDefeat();
- }
- } else {
- // Enemy defeated
- player.addExperience(enemy.experience);
- this.game.systems.economy.addCredits(enemy.credits, 'dungeon');
- player.incrementKills();
- this.stats.totalEnemiesDefeated++;
-
- // Update quest progress for combat objectives
- if (this.game.systems.questSystem) {
- this.game.systems.questSystem.onEnemyDefeated();
- }
-
- combatLog.push(`${enemy.name} defeated! +${enemy.experience} XP, +${enemy.credits} credits`);
-
- // Update UI after enemy defeat
- this.displayDungeon();
- // Also update global player UI to ensure health bars are updated
- if (player.updateUI) {
- player.updateUI();
- }
-
- // Small delay to make the combat visible
- await new Promise(resolve => setTimeout(resolve, 500));
- }
- }
-
- // Show combat results
- this.game.showNotification('Combat completed!', 'success', 3000);
- combatLog.forEach(message => this.game.showNotification(message, 'info', 2000));
-
- // Check if all enemies defeated
- const remainingEnemies = this.currentRoom.enemies.filter(e => e.health > 0);
- if (remainingEnemies.length === 0) {
- this.completeRoom();
- }
-
- this.isExploring = false;
- this.displayDungeon();
- } catch (error) {
- console.error('[DUNGEON] Error in simulateCombat:', error);
- console.error('[DUNGEON] Error stack:', error.stack);
- this.isExploring = false;
- }
- }
-
- // Health system methods
- applyShipDamage(amount) {
- const player = this.game.systems.player;
- player.ship.health = Math.max(0, player.ship.health - amount);
-
- // Only update player UI if in multiplayer mode or game is actively running
- if (this.game.shouldUpdateGUI()) {
- player.updateUI();
- }
-
- // Check for ship destruction
- if (player.ship.health <= 0) {
- this.handleShipDestruction();
- }
- }
-
- handleShipDestruction() {
- const player = this.game.systems.player;
-
- // Show destruction message
- this.game.showNotification('Your ship has been destroyed!', 'error', 5000);
- this.game.showNotification('Dungeon failed - all progress lost.', 'warning', 4000);
-
- // Remove the current ship from inventory
- if (this.game.systems.inventory) {
- this.game.systems.inventory.removeItem(player.ship);
- }
-
- // Reset to a basic ship if available in inventory
- const availableShips = this.game.systems.inventory.getItemsByType('ship');
- if (availableShips.length > 0) {
- player.ship = availableShips[0];
- this.game.showNotification(`Switched to ${player.ship.name}`, 'info', 3000);
- } else {
- // No ships available - create a basic one
- player.ship = {
- name: 'Basic Fighter',
- health: 1000,
- maxHealth: 1000,
- defense: 5,
- attack: 10,
- type: 'ship'
- };
- this.game.showNotification('No ships available - using emergency fighter', 'warning', 3000);
- }
-
- // Apply dungeon failure penalty
- this.applyDungeonFailurePenalty();
-
- // Exit dungeon
- this.exitDungeon();
- }
-
- applyDungeonFailurePenalty() {
- const player = this.game.systems.player;
-
- // Lose some credits as penalty
- const creditPenalty = Math.floor(this.game.systems.economy.credits * 0.1); // 10% credit loss
- this.game.systems.economy.removeCredits(creditPenalty);
-
- // Lose some experience
- const expPenalty = Math.floor(player.experience * 0.05); // 5% exp loss
- player.experience = Math.max(0, player.experience - expPenalty);
-
- // Update failure statistics
- this.stats.dungeonsFailed++;
-
- this.game.showNotification(`Dungeon penalty: -${creditPenalty} credits, -${expPenalty} XP`, 'warning', 4000);
- }
-
- handlePlayerDefeat() {
- this.game.showNotification('You have been defeated!', 'error', 5000);
- this.game.showNotification('You\'ve been forced to retreat from the dungeon.', 'warning', 4000);
-
- // Heal player to prevent death loop
- const player = this.game.systems.player;
- player.attributes.health = Math.floor(player.attributes.maxHealth * 0.5);
-
- // Exit dungeon
- this.exitDungeon();
- }
-
- exitDungeon() {
- this.currentDungeon = null;
- this.currentRoom = null;
- this.dungeonProgress = 0;
- this.isExploring = false;
-
- // Return to main view
- this.generateDungeonList();
- }
-
- claimRewards() {
- const room = this.currentRoom;
- if (!room.rewards || room.completed) return;
-
- // Give all rewards including materials
- this.game.systems.economy.giveRewards(room.rewards, 'dungeon');
-
- room.rewards = null; // Remove rewards after claiming
-
- this.game.showNotification('Rewards claimed!', 'success', 3000);
-
- // Use the proper completeRoom method to ensure consistent behavior
- this.completeRoom();
- }
-
- completeRoom() {
- const room = this.currentRoom;
- room.completed = true;
- room.explored = true;
-
- // Reset exploring flag when completing a room
- this.isExploring = false;
-
-
- // Auto-claim rewards if available
- if (room.rewards) {
- // Give credits and experience
- this.game.systems.economy.giveRewards(room.rewards, 'dungeon');
-
- // Add crafting materials to inventory
- if (room.rewards.materials && room.rewards.materials.length > 0) {
- let materialText = '';
- room.rewards.materials.forEach(material => {
- this.game.systems.inventory.addItem(material.id, material.quantity);
- materialText += `${material.quantity}x ${material.id}, `;
- });
-
- materialText = materialText.slice(0, -2);
-
- this.game.showNotification(`Materials found: ${materialText}`, 'success', 4000);
- }
-
- room.rewards = null;
- }
-
- this.game.showNotification(`${room.name} completed!`, 'success', 3000);
-
- // Check if this is a boss room - if so, automatically complete the dungeon
- if (room.type === 'boss') {
- setTimeout(() => {
- this.completeDungeon();
- }, 2000); // Small delay to show the completion message first
- } else {
- this.displayDungeon();
- }
-
- }
-
- nextRoom() {
-
- // Log all rooms for debugging
- this.currentDungeon.rooms.forEach((room, index) => {
- });
-
- if (this.currentDungeon.currentRoomIndex >= this.currentDungeon.rooms.length - 1) {
- return;
- }
-
- const oldIndex = this.currentDungeon.currentRoomIndex;
- this.currentDungeon.currentRoomIndex++;
- this.currentRoom = this.currentDungeon.rooms[this.currentDungeon.currentRoomIndex];
-
-
- this.displayDungeon();
- }
-
- completeDungeon() {
- if (!this.currentDungeon) {
- return;
- }
-
- const completionTime = Date.now() - this.currentDungeon.startTime;
- const player = this.game.systems.player;
- const economy = this.game.systems.economy;
-
- // Generate proper rewards including materials
- const baseRewards = this.generateRoomRewards(this.currentDungeon.difficulty, false);
-
- // Apply time bonus
- const timeBonus = Math.floor(completionTime < 300000 ? 50 : 0); // Bonus for completing in under 5 minutes
- baseRewards.credits += timeBonus;
- baseRewards.experience += Math.floor(timeBonus / 2);
-
- // Give rewards
- economy.giveRewards(baseRewards, 'dungeon');
- player.addExperience(baseRewards.experience);
-
- // Update statistics
- this.stats.dungeonsCompleted++;
- this.stats.totalTimeInDungeons += completionTime;
-
- // Update player dungeons cleared stat
- player.stats.dungeonsCleared++;
-
- // Update quest progress for dungeon objectives
- if (this.game.systems.questSystem) {
- // Check if this is a tutorial dungeon
- if (this.currentDungeon.type === 'tutorial') {
- // Mark tutorial dungeon as completed in player stats
- player.stats.tutorialDungeonCompleted = true;
- // Update tutorial dungeon quest progress
- this.game.systems.questSystem.updateTutorialDungeonProgress();
- } else {
- // Update regular dungeon quest progress using the standard method
- this.game.systems.questSystem.onDungeonCompleted();
- }
- }
-
- // Show completion message
- this.game.showNotification(`Dungeon completed! Time: ${this.game.formatTime(completionTime)}`, 'success', 5000);
- if (timeBonus > 0) {
- this.game.showNotification(`Time bonus: +${timeBonus} credits!`, 'info', 3000);
- }
-
- // Reset dungeon state
- this.currentDungeon = null;
- this.currentRoom = null;
- this.isExploring = false;
-
- // Return to dungeon list view
- this.showDungeonList();
- this.generateDungeonList();
-
- // Only force UI refresh if in multiplayer mode or game is actively running
- if (this.game.shouldUpdateGUI()) {
- this.updateUI();
- }
- }
-
- showDungeonList() {
- const dungeonListElement = document.getElementById('dungeonList');
- const dungeonViewElement = document.getElementById('dungeonView');
-
- if (dungeonListElement) {
- dungeonListElement.style.display = 'flex';
- }
-
- if (dungeonViewElement) {
- dungeonViewElement.style.display = 'none';
- // Clear the dungeon view content to prevent frozen display
- dungeonViewElement.innerHTML = '';
- }
- }
-
- showDungeonView() {
-
- const dungeonListElement = document.getElementById('dungeonList');
- const dungeonViewElement = document.getElementById('dungeonView');
-
-
- if (dungeonListElement) {
- dungeonListElement.style.display = 'none';
- }
-
- if (dungeonViewElement) {
- dungeonViewElement.style.display = 'flex';
- }
-
-
- // Display the current dungeon
- this.displayDungeon();
-
- }
-
- // UI updates
- updateUI() {
- // Update dungeon statistics if elements exist
- const dungeonsClearedElement = document.getElementById('dungeonsCleared');
- if (dungeonsClearedElement) {
- dungeonsClearedElement.textContent = this.stats.dungeonsCompleted;
- }
- }
-
- // Save/Load
- save() {
- return {
- stats: this.stats,
- currentDungeon: this.currentDungeon,
- currentRoom: this.currentRoom,
- dungeonProgress: this.dungeonProgress
- };
- }
-
- load(data) {
- if (data.stats) this.stats = { ...this.stats, ...data.stats };
- if (data.currentDungeon) this.currentDungeon = data.currentDungeon;
- if (data.currentRoom) this.currentRoom = data.currentRoom;
- if (data.dungeonProgress !== undefined) this.dungeonProgress = data.dungeonProgress;
- }
-}
diff --git a/Client-Server/js/systems/SkillSystem.js b/Client-Server/js/systems/SkillSystem.js
deleted file mode 100644
index 1148052..0000000
--- a/Client-Server/js/systems/SkillSystem.js
+++ /dev/null
@@ -1,596 +0,0 @@
-/**
- * Galaxy Strike Online - Skill System
- * Manages skills, progression, and specialization
- */
-
-class SkillSystem {
- constructor(gameEngine) {
- this.game = gameEngine;
-
- // Skill categories
- this.categories = {
- combat: 'Combat',
- science: 'Science',
- crafting: 'Crafting'
- };
-
- // Skill definitions
- this.skills = {
- combat: {
- weapons_mastery: {
- name: 'Weapons Mastery',
- description: 'Increases weapon damage and critical chance',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- attack: 2,
- criticalChance: 0.01
- },
- icon: 'fa-sword',
- unlocked: true
- },
- shield_techniques: {
- name: 'Shield Techniques',
- description: 'Improves defense and energy efficiency',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- defense: 2,
- maxEnergy: 5
- },
- icon: 'fa-shield-alt',
- unlocked: true
- },
- piloting: {
- name: 'Piloting',
- description: 'Enhances speed and evasion',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- speed: 2,
- criticalChance: 0.005
- },
- icon: 'fa-rocket',
- unlocked: true
- },
- tactical_analysis: {
- name: 'Tactical Analysis',
- description: 'Reveals enemy weaknesses and improves accuracy',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- criticalDamage: 0.05,
- attack: 1
- },
- icon: 'fa-brain',
- unlocked: false,
- requiredLevel: 5
- }
- },
- science: {
- engineering: {
- name: 'Engineering',
- description: 'Technical skills for ship components and machinery',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- craftingBonus: 0.08,
- shipStats: 0.03
- },
- icon: 'fa-wrench',
- unlocked: true
- },
- energy_manipulation: {
- name: 'Energy Manipulation',
- description: 'Better energy control and regeneration',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- maxEnergy: 10,
- energyRegeneration: 0.1
- },
- icon: 'fa-bolt',
- unlocked: true
- },
- alien_technology: {
- name: 'Alien Technology',
- description: 'Understanding and using alien artifacts',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- findRarity: 0.05,
- itemValue: 0.1
- },
- icon: 'fa-atom',
- unlocked: false,
- requiredLevel: 3
- },
- quantum_physics: {
- name: 'Quantum Physics',
- description: 'Advanced quantum mechanics for better equipment',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- criticalDamage: 0.1,
- attack: 3
- },
- icon: 'fa-microscope',
- unlocked: false,
- requiredLevel: 8
- },
- bio_engineering: {
- name: 'Bio-Engineering',
- description: 'Biological enhancements and healing',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- maxHealth: 15,
- healthRegeneration: 0.05
- },
- icon: 'fa-dna',
- unlocked: false,
- requiredLevel: 6
- }
- },
- crafting: {
- crafting: {
- name: 'General Crafting',
- description: 'Basic crafting skills for all items',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- craftingBonus: 0.05
- },
- icon: 'fa-hammer',
- unlocked: true
- },
- weapon_crafting: {
- name: 'Weapon Crafting',
- description: 'Create and upgrade weapons',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- craftingBonus: 0.1,
- weaponStats: 0.05
- },
- icon: 'fa-hammer',
- unlocked: true
- },
- armor_forging: {
- name: 'Armor Forging',
- description: 'Forge protective armor and shields',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- craftingBonus: 0.1,
- armorStats: 0.05
- },
- icon: 'fa-anvil',
- unlocked: true
- },
- resource_extraction: {
- name: 'Resource Extraction',
- description: 'Better resource gathering and efficiency',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- resourceBonus: 0.15,
- findResources: 0.1
- },
- icon: 'fa-gem',
- unlocked: false,
- requiredLevel: 4
- },
- engineering: {
- name: 'Engineering',
- description: 'Advanced ship modifications and systems',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- shipUpgrades: 0.2,
- systemEfficiency: 0.1
- },
- icon: 'fa-cogs',
- unlocked: false,
- requiredLevel: 7
- }
- }
- };
-
- // Skill experience rates
- this.experienceRates = {
- combat: 1.0,
- science: 0.8,
- crafting: 0.6
- };
-
- // Active buffs from skills
- this.activeBuffs = {};
- }
-
- async initialize() {
-}
-
- // Skill management
- addSkillExperience(category, skillId, amount) {
- const skill = this.skills[category]?.[skillId];
- if (!skill || skill.currentLevel >= skill.maxLevel) {
- return false;
- }
-
- skill.experience += amount;
-
- // Check for level up
- while (skill.experience >= skill.experienceToNext && skill.currentLevel < skill.maxLevel) {
- this.levelUpSkill(category, skillId);
- }
-
- this.applySkillEffects();
-return true;
- }
-
- levelUpSkill(category, skillId) {
- const skill = this.skills[category][skillId];
-
- // Handle excess experience
- const excessExperience = skill.experience - skill.experienceToNext;
-
- skill.currentLevel++;
- skill.experienceToNext = Math.floor(skill.experienceToNext * 1.5);
-
- // Set experience to excess (minimum 0)
- skill.experience = Math.max(0, excessExperience);
-
- // Apply skill effects
- this.applySkillEffects();
-
- this.game.showNotification(`${skill.name} leveled up to ${skill.currentLevel}!`, 'success', 4000);
- this.game.showNotification('Skill effects applied!', 'info', 3000);
- }
-
- upgradeSkill(category, skillId) {
- const skill = this.skills[category]?.[skillId];
- const player = this.game.systems.player;
-
- if (!skill) {
- this.game.showNotification('Skill not found', 'error', 3000);
- return false;
- }
-
- if (!skill.unlocked) {
- this.game.showNotification('Skill is locked', 'error', 3000);
- return false;
- }
-
- if (skill.currentLevel >= skill.maxLevel) {
- this.game.showNotification('Skill is at maximum level', 'warning', 3000);
- return false;
- }
-
- if (player.stats.skillPoints < 1) {
- this.game.showNotification('Not enough skill points', 'error', 3000);
- return false;
- }
-
- // Use skill point and level up
- player.stats.skillPoints--;
- this.levelUpSkill(category, skillId);
-
- // Update UI to refresh skill points display only if in multiplayer mode or game is actively running
- const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
-
- if (shouldUpdateUI) {
- this.updateUI();
- }
-
- return true;
- }
-
- unlockSkill(category, skillId) {
- const skill = this.skills[category]?.[skillId];
- const player = this.game.systems.player;
-
- if (!skill) {
- this.game.showNotification('Skill not found', 'error', 3000);
- return false;
- }
-
- if (skill.unlocked) {
- this.game.showNotification('Skill is already unlocked', 'warning', 3000);
- return false;
- }
-
- if (skill.requiredLevel && player.stats.level < skill.requiredLevel) {
- this.game.showNotification(`Requires level ${skill.requiredLevel}`, 'error', 3000);
- return false;
- }
-
- if (player.stats.skillPoints < 2) {
- this.game.showNotification('Requires 2 skill points to unlock', 'error', 3000);
- return false;
- }
-
- // Unlock skill
- player.stats.skillPoints -= 2;
- skill.unlocked = true;
- skill.currentLevel = 1;
-
- this.applySkillEffects();
-
- // Update UI to refresh skill points display only if in multiplayer mode or game is actively running
- const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
-
- if (shouldUpdateUI) {
- this.updateUI();
- }
-
- this.game.showNotification(`${skill.name} unlocked!`, 'success', 4000);
- return true;
- }
-
- applySkillEffects() {
- const player = this.game.systems.player;
-
- // Reset to base stats first
- this.resetToBaseStats();
-
- // Apply all skill effects
- Object.values(this.skills).forEach(skill => {
- if (skill.level > 0) {
- const skillData = this.skillData[skill.id];
- if (skillData && skillData.effects) {
- Object.entries(skillData.effects).forEach(([effect, value]) => {
- const totalEffect = value * skill.level;
-
- switch (effect) {
- case 'attack':
- player.attributes.attack += totalEffect;
- break;
- case 'defense':
- player.attributes.defense += totalEffect;
- break;
- case 'speed':
- player.attributes.speed += totalEffect;
- break;
- case 'maxHealth':
- player.attributes.maxHealth += totalEffect;
- break;
- case 'maxEnergy':
- player.attributes.maxEnergy += totalEffect;
- break;
- case 'criticalChance':
- player.attributes.criticalChance += totalEffect;
- break;
- case 'criticalDamage':
- player.attributes.criticalDamage += totalEffect;
- break;
- case 'energyRegeneration':
- case 'healthRegeneration':
- case 'craftingBonus':
- case 'weaponStats':
- case 'armorStats':
- case 'resourceBonus':
- case 'shipUpgrades':
- case 'systemEfficiency':
- case 'findRarity':
- case 'itemValue':
- case 'findResources':
- // Store these for other systems to use
- if (!this.activeBuffs[effect]) {
- this.activeBuffs[effect] = 0;
- }
- this.activeBuffs[effect] += totalEffect;
- break;
- }
- });
- }
- }
- });
-
- player.updateUI();
- }
-
- resetToBaseStats() {
- const player = this.game.systems.player;
-
- // Reset to base values (would need to store base stats separately)
- // For now, we'll use initial values
- const baseStats = {
- attack: 10 + (player.stats.level - 1) * 2,
- defense: 5 + (player.stats.level - 1) * 1,
- speed: 10,
- maxHealth: 100 + (player.stats.level - 1) * 10,
- maxEnergy: 100 + (player.stats.level - 1) * 5,
- criticalChance: 0.05,
- criticalDamage: 1.5
- };
-
- Object.assign(player.attributes, baseStats);
- this.activeBuffs = {};
- }
-
- // Skill experience from actions
- awardCombatExperience(amount) {
- this.addSkillExperience('combat', 'weapons_mastery', amount);
- this.addSkillExperience('combat', 'tactical_analysis', amount * 0.5);
- }
-
- awardScienceExperience(amount) {
- this.addSkillExperience('science', 'energy_manipulation', amount);
- this.addSkillExperience('science', 'alien_technology', amount * 0.3);
- }
-
- awardCraftingExperience(amount) {
- this.addSkillExperience('crafting', 'weapon_crafting', amount);
- this.addSkillExperience('crafting', 'armor_forging', amount * 0.5);
- }
-
- // Skill checks
- getSkillLevel(category, skillId) {
- return this.skills[category]?.[skillId]?.currentLevel || 0;
- }
-
- hasSkill(category, skillId, minimumLevel = 1) {
- const skill = this.skills[category]?.[skillId];
- return skill && skill.unlocked && skill.currentLevel >= minimumLevel;
- }
-
- getSkillBonus(effect) {
- return this.activeBuffs[effect] || 0;
- }
-
- // UI updates
- updateUI() {
- this.updateSkillsGrid();
- this.updateSkillPointsDisplay();
- }
-
- updateSkillsGrid() {
- const skillsGridElement = document.getElementById('skillsGrid');
- if (!skillsGridElement) return;
-
- const activeCategory = document.querySelector('.skill-cat-btn.active')?.dataset.category || 'combat';
- const skills = this.skills[activeCategory] || {};
-
- skillsGridElement.innerHTML = '';
-
- Object.entries(skills).forEach(([skillId, skill]) => {
- const skillElement = document.createElement('div');
- skillElement.className = `skill-item ${!skill.unlocked ? 'locked' : ''}`;
-
- const progressPercent = skill.currentLevel > 0 ?
- (skill.experience / skill.experienceToNext) * 100 : 0;
-
- // Use texture manager for icon fallback
- const iconClass = this.game.systems.textureManager ?
- this.game.systems.textureManager.getIcon(skill.icon) :
- (skill.icon || 'fa-question');
-
- skillElement.innerHTML = `
-
- ${skill.description}
- ${skill.currentLevel > 0 && skill.currentLevel < skill.maxLevel ? `
-
-
-
${skill.experience}/${skill.experienceToNext} XP
-
- ` : skill.currentLevel >= skill.maxLevel ? `
-
- MAX LEVEL
-
- ` : ''}
-
- ${!skill.unlocked ? `
-
- Unlock (2 Points)
-
- ` : skill.currentLevel < skill.maxLevel ? `
-
- Upgrade (1 Point)
-
- ` : `
- MAX LEVEL
- `}
-
- ${skill.requiredLevel && !skill.unlocked ? `
- Requires Level ${skill.requiredLevel}
- ` : ''}
- `;
-
- skillsGridElement.appendChild(skillElement);
- });
- }
-
- updateSkillPointsDisplay() {
- const player = this.game.systems.player;
- // Update skill points display if element exists
- const skillPointsElements = document.querySelectorAll('.skill-points');
- skillPointsElements.forEach(element => {
- element.textContent = `Skill Points: ${player.stats.skillPoints}`;
- });
- }
-
- // Save/Load
- save() {
- return {
- skills: this.skills,
- activeBuffs: this.activeBuffs
- };
- }
-
- load(data) {
- if (data.skills) {
- // Deep merge to preserve structure
- for (const [category, skills] of Object.entries(data.skills)) {
- if (this.skills[category]) {
- for (const [skillId, skillData] of Object.entries(skills)) {
- if (this.skills[category][skillId]) {
- Object.assign(this.skills[category][skillId], skillData);
- }
- }
- }
- }
- }
-
- if (data.activeBuffs) {
- this.activeBuffs = data.activeBuffs;
- }
-
- this.applySkillEffects();
-}
-
-reset() {
- this.skillPoints = 0;
- this.unlockedSkills = [];
- this.activeBuffs = [];
- // Skills are already defined in constructor, just reset levels
- Object.values(this.skills).forEach(category => {
- Object.values(category).forEach(skill => {
- skill.currentLevel = 0;
- skill.experience = 0;
- });
- });
-}
-
-clear() {
- this.reset();
-}
-}
diff --git a/Client/data/starbase-layout.json b/Client/data/starbase-layout.json
new file mode 100644
index 0000000..955519e
--- /dev/null
+++ b/Client/data/starbase-layout.json
@@ -0,0 +1,120 @@
+{
+ "_readme": [
+ "Galaxy Strike Online — Starbase World Layout",
+ "Edit this file to customize your starbase. Changes take effect next time you visit the Starbases tab.",
+ "",
+ "GRID",
+ " cols / rows : overall size of the world (min 8×8, max ~32×26 before performance drops)",
+ "",
+ "STYLE (global defaults — all optional hex strings)",
+ " wallColor / wallColorLeft / wallColorRight / wallColorTop",
+ " floorColorEven / floorColorOdd",
+ " doorColor / doorFrameColor",
+ "",
+ "WALLS — each entry draws a run of wall tiles",
+ " col, row : start position",
+ " span : number of tiles (default 1)",
+ " dir : 'h' horizontal | 'v' vertical",
+ " color / colorLeft / colorRight / colorTop : per-segment color overrides",
+ "",
+ "DOORS — walkable openings; panel slides up when player is adjacent",
+ " col, row : position",
+ " color : panel color override",
+ " frameColor : pillar/frame color override",
+ "",
+ "ROOMS — named regions; rendered as ghost labels + used for per-room wallpapers",
+ " id : unique string (used by the unlock / wallpaper system)",
+ " label : display text",
+ " bounds : { col, row, cols, rows }",
+ " unlock : item id required to unlock this room (omit = always open)",
+ " Locked rooms are filled with sealed-wall tiles and shown as 'LOCKED'",
+ "",
+ "PLAYER START",
+ " col, row : spawn tile (must be walkable floor)"
+ ],
+
+ "name": "Starbase Alpha-7",
+
+ "grid": { "cols": 26, "rows": 20 },
+
+ "style": {
+ "wallColor": "#00d4ff",
+ "wallColorLeft": "#0c1626",
+ "wallColorRight": "#0a1220",
+ "wallColorTop": "#1a2840",
+ "floorColorEven": "#151c2e",
+ "floorColorOdd": "#111827",
+ "doorColor": "#00ffcc",
+ "doorFrameColor": "#00d4ff"
+ },
+
+ "walls": [
+ { "col": 0, "row": 0, "span": 26, "dir": "h", "_": "top wall" },
+ { "col": 0, "row": 19, "span": 26, "dir": "h", "_": "bottom wall" },
+ { "col": 0, "row": 0, "span": 20, "dir": "v", "_": "left wall" },
+ { "col": 25, "row": 0, "span": 20, "dir": "v", "_": "right wall" },
+
+ { "col": 1, "row": 6, "span": 24, "dir": "h", "_": "main hall separator",
+ "color": "#00d4ff", "colorLeft": "#0d1a2e", "colorRight": "#0a1525", "colorTop": "#182a42" },
+
+ { "col": 7, "row": 7, "span": 13, "dir": "v", "_": "left inner wall" },
+ { "col": 17, "row": 7, "span": 2, "dir": "v", "_": "right stub top" },
+ { "col": 17, "row": 10, "span": 10, "dir": "v", "_": "right stub bottom",
+ "color": "#4488ff", "colorLeft": "#0a1830", "colorRight": "#080e20", "colorTop": "#102040" },
+
+ { "col": 7, "row": 13, "span": 10, "dir": "h", "_": "operations divider",
+ "color": "#ff00ff", "colorLeft": "#1a0a20", "colorRight": "#120616", "colorTop": "#200a30" },
+
+ { "col": 17, "row": 13, "span": 6, "dir": "h", "_": "vault corridor wall",
+ "color": "#ffcc00", "colorLeft": "#1a1200", "colorRight": "#140e00", "colorTop": "#221800" }
+ ],
+
+ "doors": [
+ { "col": 13, "row": 6, "dir": "h", "_": "main hall → command centre" },
+ { "col": 17, "row": 6, "dir": "h", "color": "#4488ff", "frameColor": "#0066ff", "_": "main hall → right wing" },
+ { "col": 7, "row": 10, "dir": "v", "_": "left wing ↔ command centre" },
+ { "col": 17, "row": 9, "dir": "v", "color": "#ff88ff", "frameColor": "#cc44cc", "_": "command centre ↔ right wing" },
+ { "col": 7, "row": 15, "dir": "v", "_": "left wing → operations" },
+ { "col": 13, "row": 13, "dir": "h", "color": "#ff00ff", "frameColor": "#cc00cc", "_": "command centre → operations" },
+ { "col": 20, "row": 13, "dir": "h", "color": "#ffcc00", "frameColor": "#ddaa00", "_": "right wing → vault corridor" }
+ ],
+
+ "rooms": [
+ {
+ "id": "main_hall",
+ "label": "Main Hall",
+ "bounds": { "col": 1, "row": 1, "cols": 24, "rows": 5 }
+ },
+ {
+ "id": "left_wing",
+ "label": "Armory Wing",
+ "bounds": { "col": 1, "row": 7, "cols": 6, "rows": 12 },
+ "unlock": "room_armory"
+ },
+ {
+ "id": "command_centre",
+ "label": "Command Centre",
+ "bounds": { "col": 8, "row": 7, "cols": 9, "rows": 6 }
+ },
+ {
+ "id": "right_wing",
+ "label": "Research Lab",
+ "bounds": { "col": 18, "row": 7, "cols": 7, "rows": 6 },
+ "unlock": "room_research_lab"
+ },
+ {
+ "id": "operations",
+ "label": "Operations Centre",
+ "bounds": { "col": 8, "row": 14, "cols": 9, "rows": 5 },
+ "unlock": "room_operations"
+ },
+ {
+ "id": "commanders_vault",
+ "label": "Commander's Vault",
+ "bounds": { "col": 18, "row": 14, "cols": 7, "rows": 5 },
+ "unlock": "room_vault"
+ }
+ ],
+
+ "playerStart": { "col": 13, "row": 3 }
+}
diff --git a/Client/index.html b/Client/index.html
index ac08ce0..3963f18 100644
--- a/Client/index.html
+++ b/Client/index.html
@@ -9,7 +9,6 @@
-
@@ -297,6 +296,22 @@
Shop
+
+
+ Fleet
+
+
+
+ Galaxy
+
+
+
+ Research
+
+
+
+ Ranks
+
@@ -419,35 +434,16 @@
-
-
-
-
Base Information
-
-
- Power Usage:
- 0/100
-
-
- Storage:
- 1000
-
-
- Production Rate:
- 0/s
-
-
-
-
-
-
-
-
+
+
Your Starbase
+ ⟳ Refresh
+
+
+
@@ -522,23 +518,23 @@
-
+
-
-
-
Starbase Management
-
-
-
-
-
-
Available Starbases
-
-
+
+
@@ -669,6 +665,222 @@
+
+
+
+
+
+
+
+
+
+ Reset View
+ Home Sector
+
+
+
+
+
+
+
+
+
⚗ Active Research Bonuses
+
+
+
+
+
Branches
+ All
+ ⚔ Weapons
+ ⚙ Engineering
+ 💰 Economy
+ 🔭 Exploration
+ 🛡 Defense
+
+
+
+
+
+
+
+
+
🏆 Commander Rankings
+
+ Level
+ Credits
+ Quests
+ Dungeons
+ Kills
+
+
+
Select a category above to view rankings.
+
+
+
@@ -706,6 +918,7 @@
+
@@ -734,165 +947,31 @@
window.game.systems.economy.updateShopUI();
}
break;
+ case 'fleet':
+ GSO_Fleet.load();
+ break;
+ case 'galaxy':
+ GSO_Galaxy.load();
+ break;
+ case 'research':
+ GSO_Research.load();
+ break;
+ case 'leaderboard':
+ GSO_Leaderboard.load(GSO_Leaderboard.currentCat || 'level');
+ break;
+ case 'inventory':
+ GSO_Inventory.render(window.gameInitializer?.serverPlayerData);
+ break;
+ case 'base':
+ // load buildings when switching to base overview
+ setTimeout(() => {
+ const activeView = document.querySelector('.base-nav-btn.active')?.dataset.view;
+ if (!activeView || activeView === 'overview') GSO_Base.load();
+ }, 50);
+ break;
case 'quests':
- console.log('[GLOBAL] Quest system check:', {
- hasGame: !!window.game,
- hasSystems: !!(window.game?.systems),
- hasQuestSystem: !!(window.game?.systems?.questSystem),
- questSystemType: typeof window.game?.systems?.questSystem
- });
-
- // REMOVED: QuestSystem should be server-driven only
- console.log('[GLOBAL] Quests are server-driven - displaying server quest data');
-
- // Display server quest data
- const questListElement = document.getElementById('questList');
- if (questListElement) {
- // Get quest data from server playerData
- const serverPlayerData = window.gameInitializer?.serverPlayerData;
- if (serverPlayerData && serverPlayerData.quests) {
- const quests = serverPlayerData.quests;
- console.log('[GLOBAL] Displaying server quest data:', quests);
-
- // Categorize quests on client since server sends old format
- const activeQuests = quests.active || [];
- const mainQuests = activeQuests.filter(quest => quest.type === 'main');
- const dailyQuests = activeQuests.filter(quest => quest.type === 'daily');
- const weeklyQuests = activeQuests.filter(quest => quest.type === 'weekly');
- const tutorialQuests = activeQuests.filter(quest => quest.type === 'tutorial');
-
- console.log('[GLOBAL] Client-side categorization:', {
- mainQuests: mainQuests.length,
- dailyQuests: dailyQuests.length,
- weeklyQuests: weeklyQuests.length,
- tutorialQuests: tutorialQuests.length
- });
-
- // Get the currently selected quest tab type
- const activeQuestTab = document.querySelector('.quest-tab-btn.active')?.dataset.type || 'main';
- console.log('[GLOBAL] Active quest tab:', activeQuestTab);
- console.log('[GLOBAL] All quest tab buttons:', document.querySelectorAll('.quest-tab-btn'));
- console.log('[GLOBAL] Active button found:', document.querySelector('.quest-tab-btn.active'));
-
- // Force check for debugging
- document.querySelectorAll('.quest-tab-btn').forEach(btn => {
- console.log('[GLOBAL] Quest tab button:', btn.dataset.type, 'active:', btn.classList.contains('active'));
- });
-
- // Create quest HTML
- let questHTML = '
';
-
- // Show quests based on selected tab
- if (activeQuestTab === 'main' && mainQuests.length > 0) {
- questHTML += '
Main Story Quests ';
- mainQuests.forEach(quest => {
- const progress = quest.progress || 0;
- const requirement = quest.requirements?.battlesWon || 1;
- const progressPercent = (progress / requirement) * 100;
- questHTML += '
';
- questHTML += '';
- questHTML += '
' + quest.description + '
';
- questHTML += '
';
- questHTML += '
';
- questHTML += '
';
- questHTML += '
';
- questHTML += '
' + progress + '/' + requirement + ' ';
- questHTML += '
';
- questHTML += '
';
- questHTML += 'Rewards: ' + (quest.rewards?.experience || 0) + ' XP, ' + (quest.rewards?.credits || 0) + ' credits ';
- questHTML += '
';
- questHTML += '
';
- });
- }
-
- if (activeQuestTab === 'daily' && dailyQuests.length > 0) {
- questHTML += '
Daily Quests ';
- dailyQuests.forEach(quest => {
- const progress = quest.progress || 0;
- const requirement = quest.requirements?.battlesWon || 1;
- const progressPercent = (progress / requirement) * 100;
- questHTML += '
';
- questHTML += '';
- questHTML += '
' + quest.description + '
';
- questHTML += '
';
- questHTML += '
';
- questHTML += '
';
- questHTML += '
';
- questHTML += '
' + progress + '/' + requirement + ' ';
- questHTML += '
';
- questHTML += '
';
- questHTML += 'Rewards: ' + (quest.rewards?.experience || 0) + ' XP, ' + (quest.rewards?.credits || 0) + ' credits, ' + (quest.rewards?.gems || 0) + ' gems ';
- questHTML += '
';
- questHTML += '
';
- });
- }
-
- if (activeQuestTab === 'weekly' && weeklyQuests.length > 0) {
- questHTML += '
Weekly Quests ';
- weeklyQuests.forEach(quest => {
- const progress = quest.progress || 0;
- const requirement = quest.requirements?.battlesWon || 1;
- const progressPercent = (progress / requirement) * 100;
- questHTML += '
';
- questHTML += '';
- questHTML += '
' + quest.description + '
';
- questHTML += '
';
- questHTML += '
';
- questHTML += '
';
- questHTML += '
';
- questHTML += '
' + progress + '/' + requirement + ' ';
- questHTML += '
';
- questHTML += '
';
- questHTML += 'Rewards: ' + (quest.rewards?.experience || 0) + ' XP, ' + (quest.rewards?.credits || 0) + ' credits, ' + (quest.rewards?.gems || 0) + ' gems ';
- questHTML += '
';
- questHTML += '
';
- });
- }
-
- if (activeQuestTab === 'completed') {
- questHTML += '
Completed Quests ';
- questHTML += '
No completed quests yet.
';
- }
-
- if (activeQuestTab === 'failed') {
- questHTML += '
Failed Quests ';
- questHTML += '
No failed quests yet.
';
- }
-
- // Show no quests message if category is empty
- if ((activeQuestTab === 'main' && mainQuests.length === 0) ||
- (activeQuestTab === 'daily' && dailyQuests.length === 0) ||
- (activeQuestTab === 'weekly' && weeklyQuests.length === 0)) {
- questHTML += '
No quests available in this category.
';
- }
-
- questHTML += '
';
- questListElement.innerHTML = questHTML;
- } else {
- questListElement.innerHTML = '
No Quest Data Available Server quest data not found.
';
- }
- }
- break;
- case 'skills':
- if (window.game.systems.skillSystem) {
- console.log('[GLOBAL] Forcing skills UI update');
- window.game.systems.skillSystem.updateUI();
- }
- break;
- case 'crafting':
- if (window.game.systems.crafting) {
- console.log('[GLOBAL] Forcing crafting UI update');
- window.game.systems.crafting.updateUI();
- }
+ // Quest rendering is handled by updateQuestDisplay()
+ updateQuestDisplay();
break;
}
return;
@@ -905,6 +984,8 @@
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
+ // Stop galaxy canvas if navigating away
+ if (tabName !== 'galaxy' && window.GSO_Galaxy) GSO_Galaxy.stop();
// Remove active class from all nav buttons
document.querySelectorAll('.nav-btn').forEach(btn => {
@@ -925,15 +1006,15 @@
}
function switchShopCategory(category) {
- console.log('[GLOBAL] switchShopCategory called with:', category);
- console.log('[GLOBAL] switchShopCategory function starting');
-
+ // Update ItemSystem activeCategory so Economy.updateShopUI renders the right items
+ if (window.game && window.game.systems && window.game.systems.itemSystem) {
+ window.game.systems.itemSystem.activeCategory = category;
+ }
+
try {
// Try to use UIManager first
if (window.game && window.game.systems && window.game.systems.ui) {
- console.log('[GLOBAL] Using UIManager path for shop');
window.game.systems.ui.switchShopCategory(category);
- console.log('[GLOBAL] switchShopCategory function completed (UIManager path)');
return;
}
@@ -1227,7 +1308,19 @@
function switchBaseView(view) {
console.log('[GLOBAL] switchBaseView called with:', view);
-
+
+ // ── Starbase World: start/stop the canvas loop ──
+ if (view === 'starbases') {
+ _startStarbaseWorld();
+ } else {
+ _stopStarbaseWorld();
+ }
+
+ // Load buildings when switching to overview
+ if (view === 'overview' && window.GSO_Base) {
+ GSO_Base.load();
+ }
+
// Try to use BaseSystem first
if (window.game && window.game.systems && window.game.systems.base) {
window.game.systems.base.switchBaseView(view);
@@ -1255,9 +1348,904 @@
targetContent.style.display = 'block';
}
}
+
+ // ── Starbase World lifecycle ──────────────────────────────────────────
+
+ window.starbaseWorld = null;
+
+ async function _startStarbaseWorld() {
+ const canvas = document.getElementById('starbase-world-canvas');
+ if (!canvas) return;
+
+ // Size canvas to its CSS display size (DPR-aware)
+ // Only apply DPR scale once — track with a data attribute
+ const container = canvas.parentElement;
+ const dpr = window.devicePixelRatio || 1;
+ const w = container.clientWidth || 900;
+ const h = 540;
+ const needsScale = !canvas.dataset.dprApplied;
+ canvas.width = Math.round(w * dpr);
+ canvas.height = Math.round(h * dpr);
+ canvas.style.height = h + 'px';
+ if (needsScale) {
+ canvas.getContext('2d').scale(dpr, dpr);
+ canvas.dataset.dprApplied = '1';
+ }
+
+ // Always reload JSON so edits take effect on next tab visit
+ const world = await loadStarbaseWorld('starbase-world-canvas');
+
+ // Apply player name from logged-in user if available
+ if (window.gameInitializer && window.gameInitializer.currentUser) {
+ world.player.name = window.gameInitializer.currentUser.username || 'Commander';
+ }
+
+ world.start();
+ }
+
+ function _stopStarbaseWorld() {
+ if (window.starbaseWorld) window.starbaseWorld.stop();
+ document.getElementById('sb-interact-modal')?.remove();
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
` : ''}
- ${recipe.craftingTime / 1000}s
+ ${(recipe.craftingTime || 0) / 1000}s
`;
-
- recipeElement.addEventListener('click', () => {
+
+ el.addEventListener('click', () => {
this.selectRecipe(recipe.id);
this.updateCraftingDetails();
});
-
- recipeListElement.appendChild(recipeElement);
+
+ listEl.appendChild(el);
});
}
-
+
updateCraftingDetails() {
- const detailsElement = document.getElementById('craftingDetails');
- if (!detailsElement) return;
-
+ const detailsEl = document.getElementById('craftingDetails');
+ if (!detailsEl) return;
+
if (!this.selectedRecipe) {
- detailsElement.innerHTML = `
+ detailsEl.innerHTML = `
Select a Recipe
Choose a recipe from the list to see details and craft items.
-
- `;
+
`;
return;
}
-
- const recipe = this.selectedRecipe;
+
+ const recipe = this.selectedRecipe;
const canCraft = this.canCraftRecipe(recipe.id);
-
- detailsElement.innerHTML = `
+
+ detailsEl.innerHTML = `
${recipe.name}
-
${recipe.description}
-
+
${recipe.description || ''}
Requirements:
- ${recipe.requirements ? Object.entries(recipe.requirements).map(([skill, level]) =>
- `
- ${skill}
- Level ${level}
-
`
- ).join('') : '
No special requirements
'}
+ ${recipe.requirements
+ ? Object.entries(recipe.requirements).map(([s, l]) =>
+ `
+ ${s}
+ Level ${l}
+
`).join('')
+ : '
No special requirements
'}
-
Materials Needed:
- ${recipe.materials ? recipe.materials.map(mat =>
+ ${recipe.materials.map(mat =>
`
${mat.id}
x${mat.quantity}
Have: ${this.game.systems.inventory?.getItemCount(mat.id) || 0}
-
`
- ).join('') : '
No materials needed
'}
+
`).join('')}
-
Results:
- ${recipe.results ? recipe.results.map(result =>
+ ${recipe.results.map(r =>
`
- ${result.id}
- x${result.quantity}
-
`
- ).join('') : ''}
+
${r.id}
+
x${r.quantity}
+
`).join('')}
-
- ${recipe.experience} XP
+ ${recipe.experience || 0} XP
- ${recipe.craftingTime / 1000} seconds
+ ${(recipe.craftingTime || 0) / 1000} seconds
-
-
${canCraft ? 'Craft Item' : 'Cannot Craft'}
-
- `;
+ `;
}
-
+
updateCraftingInfo() {
const skillSystem = this.game.systems.skillSystem;
if (!skillSystem) return;
-
+
const craftingLevel = skillSystem.getSkillLevel('crafting');
- const craftingExp = skillSystem.getSkillExperience('crafting');
- const expNeeded = skillSystem.getExperienceNeeded('crafting');
-
- const levelElement = document.getElementById('craftingLevel');
- const expElement = document.getElementById('craftingExp');
-
- if (levelElement) levelElement.textContent = craftingLevel;
- if (expElement) expElement.textContent = `${craftingExp}/${expNeeded}`;
+ const craftingExp = skillSystem.getSkillExperience('crafting');
+ const expNeeded = skillSystem.getExperienceNeeded('crafting');
+
+ const levelEl = document.getElementById('craftingLevel');
+ const expEl = document.getElementById('craftingExp');
+ if (levelEl) levelEl.textContent = craftingLevel;
+ if (expEl) expEl.textContent = `${craftingExp}/${expNeeded}`;
}
-
+
switchCategory(category) {
this.currentCategory = category;
- this.selectedRecipe = null;
-
- // Update UI only if in multiplayer mode or game is actively running
- const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
-
- if (shouldUpdateUI) {
+ this.selectedRecipe = null;
+ if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) {
this.updateUI();
}
}
diff --git a/Client/js/systems/ItemSystem.js b/Client/js/systems/ItemSystem.js
index 53af214..6d8ae06 100644
--- a/Client/js/systems/ItemSystem.js
+++ b/Client/js/systems/ItemSystem.js
@@ -430,6 +430,13 @@ class ItemSystem {
}
}
+ /**
+ * catalog getter — alias for shopItemsByCategory, used by Economy.updateShopUI
+ */
+ get catalog() {
+ return this.shopItemsByCategory;
+ }
+
/**
* Get system statistics
*/
diff --git a/Client/js/systems/SkillSystem.js b/Client/js/systems/SkillSystem.js
index 1148052..cdce8a8 100644
--- a/Client/js/systems/SkillSystem.js
+++ b/Client/js/systems/SkillSystem.js
@@ -1,504 +1,330 @@
/**
- * Galaxy Strike Online - Skill System
- * Manages skills, progression, and specialization
+ * Galaxy Strike Online - Client Skill System
+ * Skill definitions are loaded from the server; this file handles
+ * local progression tracking, UI rendering, and skill-point spending.
*/
class SkillSystem {
constructor(gameEngine) {
this.game = gameEngine;
-
- // Skill categories
+
+ // Populated after server responds to 'get_skills'
+ this.skills = { combat: {}, science: {}, crafting: {} };
+
this.categories = {
- combat: 'Combat',
- science: 'Science',
+ combat: 'Combat',
+ science: 'Science',
crafting: 'Crafting'
};
-
- // Skill definitions
- this.skills = {
- combat: {
- weapons_mastery: {
- name: 'Weapons Mastery',
- description: 'Increases weapon damage and critical chance',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- attack: 2,
- criticalChance: 0.01
- },
- icon: 'fa-sword',
- unlocked: true
- },
- shield_techniques: {
- name: 'Shield Techniques',
- description: 'Improves defense and energy efficiency',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- defense: 2,
- maxEnergy: 5
- },
- icon: 'fa-shield-alt',
- unlocked: true
- },
- piloting: {
- name: 'Piloting',
- description: 'Enhances speed and evasion',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- speed: 2,
- criticalChance: 0.005
- },
- icon: 'fa-rocket',
- unlocked: true
- },
- tactical_analysis: {
- name: 'Tactical Analysis',
- description: 'Reveals enemy weaknesses and improves accuracy',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- criticalDamage: 0.05,
- attack: 1
- },
- icon: 'fa-brain',
- unlocked: false,
- requiredLevel: 5
- }
- },
- science: {
- engineering: {
- name: 'Engineering',
- description: 'Technical skills for ship components and machinery',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- craftingBonus: 0.08,
- shipStats: 0.03
- },
- icon: 'fa-wrench',
- unlocked: true
- },
- energy_manipulation: {
- name: 'Energy Manipulation',
- description: 'Better energy control and regeneration',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- maxEnergy: 10,
- energyRegeneration: 0.1
- },
- icon: 'fa-bolt',
- unlocked: true
- },
- alien_technology: {
- name: 'Alien Technology',
- description: 'Understanding and using alien artifacts',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- findRarity: 0.05,
- itemValue: 0.1
- },
- icon: 'fa-atom',
- unlocked: false,
- requiredLevel: 3
- },
- quantum_physics: {
- name: 'Quantum Physics',
- description: 'Advanced quantum mechanics for better equipment',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- criticalDamage: 0.1,
- attack: 3
- },
- icon: 'fa-microscope',
- unlocked: false,
- requiredLevel: 8
- },
- bio_engineering: {
- name: 'Bio-Engineering',
- description: 'Biological enhancements and healing',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- maxHealth: 15,
- healthRegeneration: 0.05
- },
- icon: 'fa-dna',
- unlocked: false,
- requiredLevel: 6
- }
- },
- crafting: {
- crafting: {
- name: 'General Crafting',
- description: 'Basic crafting skills for all items',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- craftingBonus: 0.05
- },
- icon: 'fa-hammer',
- unlocked: true
- },
- weapon_crafting: {
- name: 'Weapon Crafting',
- description: 'Create and upgrade weapons',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- craftingBonus: 0.1,
- weaponStats: 0.05
- },
- icon: 'fa-hammer',
- unlocked: true
- },
- armor_forging: {
- name: 'Armor Forging',
- description: 'Forge protective armor and shields',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- craftingBonus: 0.1,
- armorStats: 0.05
- },
- icon: 'fa-anvil',
- unlocked: true
- },
- resource_extraction: {
- name: 'Resource Extraction',
- description: 'Better resource gathering and efficiency',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- resourceBonus: 0.15,
- findResources: 0.1
- },
- icon: 'fa-gem',
- unlocked: false,
- requiredLevel: 4
- },
- engineering: {
- name: 'Engineering',
- description: 'Advanced ship modifications and systems',
- maxLevel: 10,
- currentLevel: 0,
- experience: 0,
- experienceToNext: 100,
- effects: {
- shipUpgrades: 0.2,
- systemEfficiency: 0.1
- },
- icon: 'fa-cogs',
- unlocked: false,
- requiredLevel: 7
- }
- }
- };
-
- // Skill experience rates
- this.experienceRates = {
- combat: 1.0,
- science: 0.8,
- crafting: 0.6
- };
-
- // Active buffs from skills
+
+ this.experienceRates = { combat: 1.0, science: 0.8, crafting: 0.6 };
this.activeBuffs = {};
+
+ // Loading state
+ this._loaded = false;
+ this._loading = false;
}
-
+
+ // ------------------------------------------------------------------ //
+ // Initialisation — request skill definitions from the server
+ // ------------------------------------------------------------------ //
async initialize() {
-}
-
- // Skill management
+ if (this._loaded || this._loading) return;
+ this._loading = true;
+
+ console.log('[SKILL SYSTEM] Requesting skill definitions from server');
+
+ if (!window.game?.socket) {
+ console.warn('[SKILL SYSTEM] No socket connection — skills will load when connected');
+ this._loading = false;
+ return;
+ }
+
+ try {
+ const serverSkills = await this._fetchSkillsFromServer();
+ this._applyServerDefinitions(serverSkills);
+ this._loaded = true;
+ console.log('[SKILL SYSTEM] Skill definitions loaded from server');
+ } catch (err) {
+ console.error('[SKILL SYSTEM] Failed to load skills from server:', err);
+ } finally {
+ this._loading = false;
+ }
+ }
+
+ _fetchSkillsFromServer() {
+ return new Promise((resolve, reject) => {
+ const socket = window.game.socket;
+
+ const timeout = setTimeout(() => {
+ socket.off('skills_data', handler);
+ reject(new Error('Skill data request timed out'));
+ }, 10000);
+
+ const handler = (data) => {
+ clearTimeout(timeout);
+ socket.off('skills_data', handler);
+ if (data && (Array.isArray(data) || typeof data === 'object')) {
+ resolve(data);
+ } else {
+ reject(new Error('Invalid skill data from server'));
+ }
+ };
+
+ socket.on('skills_data', handler);
+ socket.emit('get_skills');
+ });
+ }
+
+ /**
+ * Merge server skill definitions into the local skill map.
+ * Preserves any progress already loaded from playerData.
+ */
+ _applyServerDefinitions(serverSkills) {
+ // Server may return an array or a categorised object
+ const asList = Array.isArray(serverSkills)
+ ? serverSkills
+ : Object.values(serverSkills).flat();
+
+ // Reset to empty categories first
+ this.skills = { combat: {}, science: {}, crafting: {} };
+
+ for (const skill of asList) {
+ const cat = skill.category || 'combat';
+ if (!this.skills[cat]) this.skills[cat] = {};
+
+ // Keep any existing progress if already loaded from save data
+ const existing = this.skills[cat][skill.id] || {};
+ this.skills[cat][skill.id] = {
+ name: skill.name,
+ description: skill.description,
+ maxLevel: skill.maxLevel || 100,
+ effects: skill.bonuses || skill.effects || {},
+ icon: skill.icon || 'fa-star',
+ // Progress fields — kept from existing save data if present
+ currentLevel: existing.currentLevel ?? 0,
+ experience: existing.experience ?? 0,
+ experienceToNext: existing.experienceToNext ?? (skill.experiencePerLevel || 1000),
+ unlocked: existing.unlocked ?? (skill.defaultUnlocked !== false),
+ requiredLevel: skill.requiredLevel ?? null
+ };
+ }
+
+ console.log('[SKILL SYSTEM] Applied server definitions. Categories:', Object.keys(this.skills));
+ }
+
+ // ------------------------------------------------------------------ //
+ // Skill progression
+ // ------------------------------------------------------------------ //
addSkillExperience(category, skillId, amount) {
const skill = this.skills[category]?.[skillId];
- if (!skill || skill.currentLevel >= skill.maxLevel) {
- return false;
- }
-
+ if (!skill || skill.currentLevel >= skill.maxLevel) return false;
+
skill.experience += amount;
-
- // Check for level up
+
while (skill.experience >= skill.experienceToNext && skill.currentLevel < skill.maxLevel) {
this.levelUpSkill(category, skillId);
}
-
+
this.applySkillEffects();
-return true;
- }
-
- levelUpSkill(category, skillId) {
- const skill = this.skills[category][skillId];
-
- // Handle excess experience
- const excessExperience = skill.experience - skill.experienceToNext;
-
- skill.currentLevel++;
- skill.experienceToNext = Math.floor(skill.experienceToNext * 1.5);
-
- // Set experience to excess (minimum 0)
- skill.experience = Math.max(0, excessExperience);
-
- // Apply skill effects
- this.applySkillEffects();
-
- this.game.showNotification(`${skill.name} leveled up to ${skill.currentLevel}!`, 'success', 4000);
- this.game.showNotification('Skill effects applied!', 'info', 3000);
- }
-
- upgradeSkill(category, skillId) {
- const skill = this.skills[category]?.[skillId];
- const player = this.game.systems.player;
-
- if (!skill) {
- this.game.showNotification('Skill not found', 'error', 3000);
- return false;
- }
-
- if (!skill.unlocked) {
- this.game.showNotification('Skill is locked', 'error', 3000);
- return false;
- }
-
- if (skill.currentLevel >= skill.maxLevel) {
- this.game.showNotification('Skill is at maximum level', 'warning', 3000);
- return false;
- }
-
- if (player.stats.skillPoints < 1) {
- this.game.showNotification('Not enough skill points', 'error', 3000);
- return false;
- }
-
- // Use skill point and level up
- player.stats.skillPoints--;
- this.levelUpSkill(category, skillId);
-
- // Update UI to refresh skill points display only if in multiplayer mode or game is actively running
- const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
-
- if (shouldUpdateUI) {
- this.updateUI();
- }
-
return true;
}
-
- unlockSkill(category, skillId) {
- const skill = this.skills[category]?.[skillId];
+
+ levelUpSkill(category, skillId) {
+ const skill = this.skills[category][skillId];
+ const excess = skill.experience - skill.experienceToNext;
+
+ skill.currentLevel++;
+ skill.experienceToNext = Math.floor(skill.experienceToNext * 1.5);
+ skill.experience = Math.max(0, excess);
+
+ this.applySkillEffects();
+ this.game.showNotification(`${skill.name} leveled up to ${skill.currentLevel}!`, 'success', 4000);
+ }
+
+ upgradeSkill(category, skillId) {
+ const skill = this.skills[category]?.[skillId];
const player = this.game.systems.player;
-
- if (!skill) {
- this.game.showNotification('Skill not found', 'error', 3000);
- return false;
+
+ if (!skill) { this.game.showNotification('Skill not found', 'error', 3000); return false; }
+ if (!skill.unlocked) { this.game.showNotification('Skill is locked', 'error', 3000); return false; }
+ if (skill.currentLevel >= skill.maxLevel){ this.game.showNotification('Skill is at maximum level', 'warning', 3000); return false; }
+ if (player.stats.skillPoints < 1) { this.game.showNotification('Not enough skill points', 'error', 3000); return false; }
+
+ player.stats.skillPoints--;
+ this.levelUpSkill(category, skillId);
+
+ if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) {
+ this.updateUI();
}
-
- if (skill.unlocked) {
- this.game.showNotification('Skill is already unlocked', 'warning', 3000);
- return false;
- }
-
+ return true;
+ }
+
+ unlockSkill(category, skillId) {
+ const skill = this.skills[category]?.[skillId];
+ const player = this.game.systems.player;
+
+ if (!skill) { this.game.showNotification('Skill not found', 'error', 3000); return false; }
+ if (skill.unlocked) { this.game.showNotification('Skill is already unlocked', 'warning', 3000); return false; }
if (skill.requiredLevel && player.stats.level < skill.requiredLevel) {
this.game.showNotification(`Requires level ${skill.requiredLevel}`, 'error', 3000);
return false;
}
-
if (player.stats.skillPoints < 2) {
this.game.showNotification('Requires 2 skill points to unlock', 'error', 3000);
return false;
}
-
- // Unlock skill
+
player.stats.skillPoints -= 2;
skill.unlocked = true;
skill.currentLevel = 1;
-
this.applySkillEffects();
-
- // Update UI to refresh skill points display only if in multiplayer mode or game is actively running
- const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
-
- if (shouldUpdateUI) {
+
+ if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) {
this.updateUI();
}
-
this.game.showNotification(`${skill.name} unlocked!`, 'success', 4000);
return true;
}
-
+
applySkillEffects() {
const player = this.game.systems.player;
-
- // Reset to base stats first
this.resetToBaseStats();
-
- // Apply all skill effects
- Object.values(this.skills).forEach(skill => {
- if (skill.level > 0) {
- const skillData = this.skillData[skill.id];
- if (skillData && skillData.effects) {
- Object.entries(skillData.effects).forEach(([effect, value]) => {
- const totalEffect = value * skill.level;
-
- switch (effect) {
- case 'attack':
- player.attributes.attack += totalEffect;
- break;
- case 'defense':
- player.attributes.defense += totalEffect;
- break;
- case 'speed':
- player.attributes.speed += totalEffect;
- break;
- case 'maxHealth':
- player.attributes.maxHealth += totalEffect;
- break;
- case 'maxEnergy':
- player.attributes.maxEnergy += totalEffect;
- break;
- case 'criticalChance':
- player.attributes.criticalChance += totalEffect;
- break;
- case 'criticalDamage':
- player.attributes.criticalDamage += totalEffect;
- break;
- case 'energyRegeneration':
- case 'healthRegeneration':
- case 'craftingBonus':
- case 'weaponStats':
- case 'armorStats':
- case 'resourceBonus':
- case 'shipUpgrades':
- case 'systemEfficiency':
- case 'findRarity':
- case 'itemValue':
- case 'findResources':
- // Store these for other systems to use
- if (!this.activeBuffs[effect]) {
- this.activeBuffs[effect] = 0;
- }
- this.activeBuffs[effect] += totalEffect;
- break;
- }
- });
+
+ for (const category of Object.values(this.skills)) {
+ for (const skill of Object.values(category)) {
+ if (!skill.unlocked || skill.currentLevel <= 0) continue;
+
+ for (const [effect, value] of Object.entries(skill.effects || {})) {
+ const total = value * skill.currentLevel;
+ switch (effect) {
+ case 'attack': player.attributes.attack += total; break;
+ case 'defense': player.attributes.defense += total; break;
+ case 'speed': player.attributes.speed += total; break;
+ case 'health':
+ case 'maxHealth': player.attributes.maxHealth += total; break;
+ case 'maxEnergy': player.attributes.maxEnergy += total; break;
+ case 'criticalChance': player.attributes.criticalChance += total; break;
+ case 'criticalDamage': player.attributes.criticalDamage += total; break;
+ default:
+ if (!this.activeBuffs[effect]) this.activeBuffs[effect] = 0;
+ this.activeBuffs[effect] += total;
+ }
}
}
- });
-
+ }
+
player.updateUI();
}
-
+
resetToBaseStats() {
const player = this.game.systems.player;
-
- // Reset to base values (would need to store base stats separately)
- // For now, we'll use initial values
- const baseStats = {
- attack: 10 + (player.stats.level - 1) * 2,
- defense: 5 + (player.stats.level - 1) * 1,
- speed: 10,
- maxHealth: 100 + (player.stats.level - 1) * 10,
- maxEnergy: 100 + (player.stats.level - 1) * 5,
+ const lvl = player.stats.level || 1;
+ Object.assign(player.attributes, {
+ attack: 10 + (lvl - 1) * 2,
+ defense: 5 + (lvl - 1) * 1,
+ speed: 10,
+ maxHealth: 100 + (lvl - 1) * 10,
+ maxEnergy: 100 + (lvl - 1) * 5,
criticalChance: 0.05,
criticalDamage: 1.5
- };
-
- Object.assign(player.attributes, baseStats);
+ });
this.activeBuffs = {};
}
-
- // Skill experience from actions
+
+ // ------------------------------------------------------------------ //
+ // Combat / science / crafting XP helpers
+ // ------------------------------------------------------------------ //
awardCombatExperience(amount) {
- this.addSkillExperience('combat', 'weapons_mastery', amount);
+ this.addSkillExperience('combat', 'weapons_mastery', amount);
this.addSkillExperience('combat', 'tactical_analysis', amount * 0.5);
}
-
+
awardScienceExperience(amount) {
this.addSkillExperience('science', 'energy_manipulation', amount);
- this.addSkillExperience('science', 'alien_technology', amount * 0.3);
+ this.addSkillExperience('science', 'alien_technology', amount * 0.3);
}
-
+
awardCraftingExperience(amount) {
- this.addSkillExperience('crafting', 'weapon_crafting', amount);
- this.addSkillExperience('crafting', 'armor_forging', amount * 0.5);
+ this.addSkillExperience('crafting', 'weapons_crafting', amount);
+ this.addSkillExperience('crafting', 'armor_forging', amount * 0.5);
}
-
- // Skill checks
+
+ // ------------------------------------------------------------------ //
+ // Queries
+ // ------------------------------------------------------------------ //
getSkillLevel(category, skillId) {
+ // Support single-arg form used by CraftingSystem: getSkillLevel('crafting')
+ if (skillId === undefined) {
+ let max = 0;
+ for (const cat of Object.values(this.skills)) {
+ if (cat[category]) max = Math.max(max, cat[category].currentLevel || 0);
+ }
+ return max;
+ }
return this.skills[category]?.[skillId]?.currentLevel || 0;
}
-
+
+ getSkillExperience(skillId) {
+ for (const cat of Object.values(this.skills)) {
+ if (cat[skillId]) return cat[skillId].experience || 0;
+ }
+ return 0;
+ }
+
+ getExperienceNeeded(skillId) {
+ for (const cat of Object.values(this.skills)) {
+ if (cat[skillId]) return cat[skillId].experienceToNext || 0;
+ }
+ return 0;
+ }
+
hasSkill(category, skillId, minimumLevel = 1) {
const skill = this.skills[category]?.[skillId];
return skill && skill.unlocked && skill.currentLevel >= minimumLevel;
}
-
+
getSkillBonus(effect) {
return this.activeBuffs[effect] || 0;
}
-
- // UI updates
+
+ // ------------------------------------------------------------------ //
+ // UI
+ // ------------------------------------------------------------------ //
updateUI() {
this.updateSkillsGrid();
this.updateSkillPointsDisplay();
}
-
+
updateSkillsGrid() {
- const skillsGridElement = document.getElementById('skillsGrid');
- if (!skillsGridElement) return;
-
+ const grid = document.getElementById('skillsGrid');
+ if (!grid) return;
+
const activeCategory = document.querySelector('.skill-cat-btn.active')?.dataset.category || 'combat';
const skills = this.skills[activeCategory] || {};
-
- skillsGridElement.innerHTML = '';
-
+
+ if (!this._loaded) {
+ grid.innerHTML = 'Loading skills from server...
';
+ return;
+ }
+
+ grid.innerHTML = '';
+
Object.entries(skills).forEach(([skillId, skill]) => {
- const skillElement = document.createElement('div');
- skillElement.className = `skill-item ${!skill.unlocked ? 'locked' : ''}`;
-
- const progressPercent = skill.currentLevel > 0 ?
- (skill.experience / skill.experienceToNext) * 100 : 0;
-
- // Use texture manager for icon fallback
- const iconClass = this.game.systems.textureManager ?
- this.game.systems.textureManager.getIcon(skill.icon) :
- (skill.icon || 'fa-question');
-
- skillElement.innerHTML = `
+ const el = document.createElement('div');
+ el.className = `skill-item ${!skill.unlocked ? 'locked' : ''}`;
+
+ const progressPercent = skill.currentLevel > 0
+ ? (skill.experience / skill.experienceToNext) * 100
+ : 0;
+
+ const iconClass = this.game.systems.textureManager
+ ? this.game.systems.textureManager.getIcon(skill.icon)
+ : (skill.icon || 'fa-question');
+
+ el.innerHTML = `
👥 Social
+ +🤝 Add Friend
+📬 Friend Requests (0)
+👾 Friends (0 online)
++ 🌐 Faction Reputation (GDD §15.3) +↺
+
++ ⚔ Combat Log (GDD §9.5) +Refresh
+
+