Game-Server/Client/js/systems/BaseSystem.js

2196 lines
82 KiB
JavaScript

/**
* Galaxy Strike Online - Base System
* Manages player base building and customization
*/
class BaseSystem {
constructor(gameEngine) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.constructor', {
gameEngineProvided: !!gameEngine
});
this.game = gameEngine;
// Base configuration
this.base = {
name: 'Command Center Alpha',
level: 1,
experience: 0,
experienceToNext: 500,
rooms: [],
decorations: [],
power: 100,
maxPower: 100,
storage: 1000,
maxStorage: 1000
};
if (debugLogger) debugLogger.logStep('Base configuration initialized', {
baseName: this.base.name,
baseLevel: this.base.level,
initialPower: this.base.power,
maxPower: this.base.maxPower,
initialStorage: this.base.storage,
maxStorage: this.base.maxStorage
});
// Room types
this.roomTypes = {
command_center: {
name: 'Command Center',
description: 'Central control room for your operations',
size: 'large',
powerCost: 20,
storageBonus: 0,
buildCost: 0,
requiredLevel: 1,
maxLevel: 1,
icon: 'fa-satellite-dish'
},
barracks: {
name: 'Barracks',
description: 'Housing for crew and allies',
size: 'medium',
powerCost: 10,
storageBonus: 100,
buildCost: 500,
requiredLevel: 2,
maxLevel: 5,
icon: 'fa-home'
},
laboratory: {
name: 'Laboratory',
description: 'Research facility for new technologies',
size: 'medium',
powerCost: 15,
storageBonus: 50,
buildCost: 1000,
requiredLevel: 3,
maxLevel: 5,
icon: 'fa-flask'
},
workshop: {
name: 'Workshop',
description: 'Crafting and equipment modification station',
size: 'medium',
powerCost: 12,
storageBonus: 80,
buildCost: 800,
requiredLevel: 2,
maxLevel: 5,
icon: 'fa-hammer'
},
storage_bay: {
name: 'Storage Bay',
description: 'Additional storage for resources and items',
size: 'large',
powerCost: 5,
storageBonus: 500,
buildCost: 300,
requiredLevel: 1,
maxLevel: 10,
icon: 'fa-warehouse'
},
power_generator: {
name: 'Power Generator',
description: 'Generates power for base operations',
size: 'small',
powerCost: -25,
storageBonus: 0,
buildCost: 600,
requiredLevel: 2,
maxLevel: 5,
icon: 'fa-bolt'
},
defense_turret: {
name: 'Defense Turret',
description: 'Automated defense system',
size: 'small',
powerCost: 8,
storageBonus: 0,
buildCost: 400,
requiredLevel: 3,
maxLevel: 8,
icon: 'fa-crosshairs'
},
communication_array: {
name: 'Communication Array',
description: 'Long-range communication and scanning',
size: 'medium',
powerCost: 10,
storageBonus: 20,
buildCost: 700,
requiredLevel: 4,
maxLevel: 3,
icon: 'fa-broadcast-tower'
},
medical_bay: {
name: 'Medical Bay',
description: 'Health restoration and crew recovery',
size: 'medium',
powerCost: 12,
storageBonus: 30,
buildCost: 900,
requiredLevel: 3,
maxLevel: 4,
icon: 'fa-medkit'
},
recreation_room: {
name: 'Recreation Room',
description: 'Crew morale and relaxation facilities',
size: 'small',
powerCost: 6,
storageBonus: 10,
buildCost: 350,
requiredLevel: 2,
maxLevel: 3,
icon: 'fa-gamepad'
}
};
// Base upgrades
this.upgrades = {
power_efficiency: {
name: 'Power Efficiency',
description: 'Reduces power consumption of all rooms',
cost: 1000,
effect: { powerReduction: 0.1 },
maxLevel: 5,
currentLevel: 0
},
storage_expansion: {
name: 'Storage Expansion',
description: 'Increases inventory slots for item storage',
cost: 5000,
maxLevel: 10,
currentLevel: 0,
icon: 'fa-boxes',
slotBonus: 5 // +5 slots per level
},
automation_systems: {
name: 'Automation Systems',
description: 'Automated resource collection and processing',
cost: 1500,
effect: {
productionBonus: 0.2 // +20% production
},
maxLevel: 5,
currentLevel: 0
},
advanced_defenses: {
name: 'Advanced Defenses',
description: 'Improved base defense systems',
cost: 2000,
effect: { defenseBonus: 10 },
maxLevel: 3,
currentLevel: 0
}
};
// Base grid layout (10x10)
this.gridSize = 10;
this.grid = [];
if (debugLogger) debugLogger.logStep('Grid configuration initialized', {
gridSize: this.gridSize,
gridInitialized: false
});
// Initialize grid
this.initializeGrid();
if (debugLogger) debugLogger.endStep('BaseSystem.constructor', {
roomTypesCount: Object.keys(this.roomTypes).length,
upgradesCount: Object.keys(this.upgrades).length,
gridSize: this.gridSize,
gridInitialized: !!this.grid,
baseConfiguration: this.base
});
}
initializeBaseData() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.initializeBaseData', {
oldBaseName: this.base.name,
oldBaseLevel: this.base.level
});
// Initialize base with default values
this.base = {
name: 'Command Center Alpha',
level: 1,
experience: 0,
experienceToNext: 500,
rooms: [],
decorations: [],
power: 100,
maxPower: 100,
storage: 1000,
maxStorage: 1000
};
// Initialize resources
this.resources = {
energy: 100,
materials: 50,
research: 0
};
if (debugLogger) debugLogger.endStep('BaseSystem.initializeBaseData', {
newBaseName: this.base.name,
newBaseLevel: this.base.level,
roomsCount: this.base.rooms.length,
decorationsCount: this.base.decorations.length,
resourcesInitialized: this.resources
});
}
initialize() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.initialize', {
currentBaseLevel: this.base.level,
roomsCount: this.base.rooms.length
});
// Initialize base data
if (debugLogger) debugLogger.logStep('Initializing base data');
this.initializeBaseData();
// Initialize inventory bonus slots on game start
if (debugLogger) debugLogger.logStep('Updating inventory bonus slots');
this.updateInventoryBonusSlots();
if (debugLogger) debugLogger.endStep('BaseSystem.initialize', {
baseDataInitialized: true,
inventoryBonusSlotsUpdated: true
});
}
async initialize() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.asyncInitialize', {
currentBaseLevel: this.base.level,
roomsCount: this.base.rooms.length
});
// Start with command center
if (debugLogger) debugLogger.logStep('Adding command center room');
this.addRoom('command_center', 5, 5);
// Set up base navigation
if (debugLogger) debugLogger.logStep('Setting up base navigation');
this.setupBaseNavigation();
// Initialize ship gallery
if (debugLogger) debugLogger.logStep('Initializing ship gallery');
this.initializeShipGallery();
// Initialize starbase system
if (debugLogger) debugLogger.logStep('Initializing starbase system');
this.initializeStarbaseSystem();
if (debugLogger) debugLogger.endStep('BaseSystem.asyncInitialize', {
commandCenterAdded: true,
navigationSetup: true,
shipGalleryInitialized: true,
starbaseSystemInitialized: true
});
}
initializeGrid() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.initializeGrid', {
gridSize: this.gridSize
});
this.grid = [];
for (let y = 0; y < this.gridSize; y++) {
this.grid[y] = [];
for (let x = 0; x < this.gridSize; x++) {
this.grid[y][x] = null;
}
}
if (debugLogger) debugLogger.endStep('BaseSystem.initializeGrid', {
gridSize: this.gridSize,
gridCreated: true,
totalCells: this.gridSize * this.gridSize
});
}
// Room management
getRoomAt(x, y) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.log('BaseSystem.getRoomAt called', {
x: x,
y: y,
gridSize: this.gridSize
});
// Check if coordinates are within grid bounds
if (x < 0 || x >= this.gridSize || y < 0 || y >= this.gridSize) {
if (debugLogger) debugLogger.log('Coordinates out of bounds', {
x: x,
y: y,
gridSize: this.gridSize
});
return null;
}
// Ensure grid exists
if (!this.grid || !this.grid[y]) {
console.log('[BASE SYSTEM] Grid not properly initialized in getRoomAt');
if (debugLogger) debugLogger.log('Grid not properly initialized', {
gridExists: !!this.grid,
gridRowExists: !!(this.grid && this.grid[y]),
y: y
});
return null;
}
// Get room ID from grid
const roomId = this.grid[y][x];
if (!roomId) {
if (debugLogger) debugLogger.log('No room at coordinates', {
x: x,
y: y,
roomId: roomId
});
return null;
}
// Find room by ID
const room = this.base.rooms.find(r => r.id === roomId) || null;
if (debugLogger) debugLogger.log('Room found at coordinates', {
x: x,
y: y,
roomId: roomId,
roomFound: !!room,
roomName: room ? room.name : null,
roomType: room ? room.type : null
});
return room;
}
canBuildRoom(roomType, x, y) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.canBuildRoom', {
roomType: roomType,
x: x,
y: y,
currentBaseLevel: this.base.level,
currentCredits: this.game.systems.economy.credits,
currentRooms: this.base.rooms.length
});
const roomTemplate = this.roomTypes[roomType];
if (!roomTemplate) {
if (debugLogger) debugLogger.endStep('BaseSystem.canBuildRoom', {
success: false,
reason: 'Room template not found',
roomType: roomType
});
return false;
}
if (debugLogger) debugLogger.logStep('Room template found', {
roomName: roomTemplate.name,
requiredLevel: roomTemplate.requiredLevel,
buildCost: roomTemplate.buildCost,
powerCost: roomTemplate.powerCost,
maxLevel: roomTemplate.maxLevel
});
// Check player level
if (this.game.systems.player.stats.level < roomTemplate.requiredLevel) {
if (debugLogger) debugLogger.endStep('BaseSystem.canBuildRoom', {
success: false,
reason: 'Player level too low',
playerLevel: this.game.systems.player.stats.level,
requiredLevel: roomTemplate.requiredLevel
});
return false;
}
// Check resources
if (this.game.systems.economy.credits < roomTemplate.buildCost) {
if (debugLogger) debugLogger.endStep('BaseSystem.canBuildRoom', {
success: false,
reason: 'Insufficient credits',
currentCredits: this.game.systems.economy.credits,
requiredCredits: roomTemplate.buildCost
});
return false;
}
// Check if room already exists at max level
const existingRoom = this.base.rooms.find(r => r.type === roomType);
if (existingRoom && existingRoom.level >= roomTemplate.maxLevel) {
if (debugLogger) debugLogger.endStep('BaseSystem.canBuildRoom', {
success: false,
reason: 'Room already at max level',
roomType: roomType,
currentLevel: existingRoom.level,
maxLevel: roomTemplate.maxLevel
});
return false;
}
// Check grid placement
const roomSize = this.getRoomSize(roomTemplate.size);
if (!this.canPlaceRoom(x, y, roomSize.width, roomSize.height)) {
if (debugLogger) debugLogger.endStep('BaseSystem.canBuildRoom', {
success: false,
reason: 'Cannot place room at location',
x: x,
y: y,
roomSize: roomSize
});
return false;
}
// Check power availability
const powerRequirement = roomTemplate.powerCost;
if (this.base.power + powerRequirement > this.base.maxPower) {
if (debugLogger) debugLogger.endStep('BaseSystem.canBuildRoom', {
success: false,
reason: 'Insufficient power',
currentPower: this.base.power,
powerRequirement: powerRequirement,
maxPower: this.base.maxPower,
totalPowerNeeded: this.base.power + powerRequirement
});
return false;
}
if (debugLogger) debugLogger.endStep('BaseSystem.canBuildRoom', {
success: true,
roomType: roomType,
roomName: roomTemplate.name,
x: x,
y: y,
isUpgrade: !!existingRoom,
currentLevel: existingRoom ? existingRoom.level : 0
});
return true;
}
getRoomSize(size) {
const sizes = {
small: { width: 1, height: 1 },
medium: { width: 2, height: 2 },
large: { width: 3, height: 3 }
};
return sizes[size] || sizes.medium;
}
canPlaceRoom(x, y, width, height) {
// Check bounds
if (x < 0 || y < 0 || x + width > this.gridSize || y + height > this.gridSize) {
return false;
}
// Check for collisions
for (let dy = 0; dy < height; dy++) {
for (let dx = 0; dx < width; dx++) {
if (this.grid[y + dy][x + dx] !== null) {
return false;
}
}
}
return true;
}
addRoom(roomType, x, y) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.addRoom', {
roomType: roomType,
x: x,
y: y,
currentRoomsCount: this.base.rooms.length,
currentCredits: this.game.systems.economy.credits
});
if (!this.canBuildRoom(roomType, x, y)) {
if (debugLogger) debugLogger.endStep('BaseSystem.addRoom', {
success: false,
reason: 'Cannot build room - failed validation',
roomType: roomType,
x: x,
y: y
});
return false;
}
const roomTemplate = this.roomTypes[roomType];
const roomSize = this.getRoomSize(roomTemplate.size);
if (debugLogger) debugLogger.logStep('Room validation passed', {
roomName: roomTemplate.name,
roomSize: roomSize,
buildCost: roomTemplate.buildCost
});
// Check if upgrading existing room
const existingRoom = this.base.rooms.find(r => r.type === roomType);
const isUpgrade = !!existingRoom;
if (isUpgrade) {
if (debugLogger) debugLogger.logStep('Upgrading existing room', {
roomId: existingRoom.id,
currentLevel: existingRoom.level,
newLevel: existingRoom.level + 1
});
// Upgrade existing room
existingRoom.level++;
existingRoom.powerCost = roomTemplate.powerCost * existingRoom.level;
existingRoom.storageBonus = roomTemplate.storageBonus * existingRoom.level;
} else {
if (debugLogger) debugLogger.logStep('Creating new room', {
roomType: roomType,
roomName: roomTemplate.name,
x: x,
y: y,
roomSize: roomSize
});
// Create new room
const room = {
id: Date.now().toString(),
type: roomType,
name: roomTemplate.name,
level: 1,
x: x,
y: y,
width: roomSize.width,
height: roomSize.height,
powerCost: roomTemplate.powerCost,
storageBonus: roomTemplate.storageBonus,
icon: roomTemplate.icon,
description: roomTemplate.description
};
this.base.rooms.push(room);
// Place on grid
for (let dy = 0; dy < roomSize.height; dy++) {
for (let dx = 0; dx < roomSize.width; dx++) {
this.grid[y + dy][x + dx] = room.id;
}
}
if (debugLogger) debugLogger.logStep('New room placed on grid', {
roomId: room.id,
gridPositions: roomSize.width * roomSize.height
});
}
// Update base stats
if (debugLogger) debugLogger.logStep('Updating base stats');
this.updateBaseStats();
// Deduct cost
const oldCredits = this.game.systems.economy.credits;
this.game.systems.economy.removeCredits(roomTemplate.buildCost);
if (debugLogger) debugLogger.logStep('Build cost deducted', {
buildCost: roomTemplate.buildCost,
oldCredits: oldCredits,
newCredits: this.game.systems.economy.credits
});
this.game.showNotification(`${roomTemplate.name} ${isUpgrade ? 'upgraded' : 'built'}!`, 'success', 3000);
if (debugLogger) debugLogger.endStep('BaseSystem.addRoom', {
success: true,
roomType: roomType,
roomName: roomTemplate.name,
isUpgrade: isUpgrade,
roomId: isUpgrade ? existingRoom.id : this.base.rooms[this.base.rooms.length - 1].id,
newLevel: isUpgrade ? existingRoom.level : 1,
finalRoomsCount: this.base.rooms.length,
buildCost: roomTemplate.buildCost,
finalCredits: this.game.systems.economy.credits
});
return true;
}
removeRoom(roomId) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.removeRoom', {
roomId: roomId,
currentRoomsCount: this.base.rooms.length
});
const roomIndex = this.base.rooms.findIndex(r => r.id === roomId);
if (roomIndex === -1) {
if (debugLogger) debugLogger.endStep('BaseSystem.removeRoom', {
success: false,
reason: 'Room not found',
roomId: roomId
});
return false;
}
const room = this.base.rooms[roomIndex];
if (debugLogger) debugLogger.logStep('Room found for removal', {
roomId: room.id,
roomName: room.name,
roomType: room.type,
roomLevel: room.level,
roomPosition: { x: room.x, y: room.y }
});
// Don't allow removing command center
if (room.type === 'command_center') {
this.game.showNotification('Cannot remove Command Center', 'error', 3000);
if (debugLogger) debugLogger.endStep('BaseSystem.removeRoom', {
success: false,
reason: 'Cannot remove command center',
roomId: roomId,
roomType: room.type
});
return false;
}
// Remove from grid
let gridCellsCleared = 0;
for (let dy = 0; dy < room.height; dy++) {
for (let dx = 0; dx < room.width; dx++) {
this.grid[room.y + dy][room.x + dx] = null;
gridCellsCleared++;
}
}
if (debugLogger) debugLogger.logStep('Room removed from grid', {
gridCellsCleared: gridCellsCleared,
roomSize: { width: room.width, height: room.height }
});
// Remove from rooms array
this.base.rooms.splice(roomIndex, 1);
if (debugLogger) debugLogger.logStep('Room removed from rooms array', {
removedIndex: roomIndex,
remainingRoomsCount: this.base.rooms.length
});
// Update base stats
if (debugLogger) debugLogger.logStep('Updating base stats after removal');
this.updateBaseStats();
// Refund some resources
const refundAmount = Math.floor(this.roomTypes[room.type].buildCost * 0.5);
const oldCredits = this.game.systems.economy.credits;
this.game.systems.economy.addCredits(refundAmount, 'room_refund');
if (debugLogger) debugLogger.logStep('Refund processed', {
refundAmount: refundAmount,
oldCredits: oldCredits,
newCredits: this.game.systems.economy.credits,
originalBuildCost: this.roomTypes[room.type].buildCost,
refundPercentage: 0.5
});
this.game.showNotification(`${room.name} removed. Refunded ${refundAmount} credits`, 'info', 3000);
if (debugLogger) debugLogger.endStep('BaseSystem.removeRoom', {
success: true,
roomId: roomId,
roomName: room.name,
roomType: room.type,
refundAmount: refundAmount,
finalRoomsCount: this.base.rooms.length,
finalCredits: this.game.systems.economy.credits
});
return true;
}
calculateBaseStats() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.calculateBaseStats', {
currentRoomsCount: this.base.rooms.length,
currentPower: this.base.power,
currentMaxPower: this.base.maxPower,
currentStorage: this.base.storage,
currentMaxStorage: this.base.maxStorage
});
let totalPowerCost = 0;
let totalStorageBonus = 0;
// Calculate power cost and storage bonus from rooms
this.base.rooms.forEach(room => {
const roomType = this.roomTypes[room.type];
const roomPowerCost = roomType.powerCost * room.level;
const roomStorageBonus = roomType.storageBonus * room.level;
totalPowerCost += roomPowerCost;
totalStorageBonus += roomStorageBonus;
if (debugLogger) debugLogger.logStep('Room contribution calculated', {
roomId: room.id,
roomType: room.type,
roomName: room.name,
roomLevel: room.level,
powerCost: roomPowerCost,
storageBonus: roomStorageBonus
});
});
// Add upgrade bonuses
const storageExpansion = (this.upgrades.storage_expansion?.currentLevel || 0) * (this.upgrades.storage_expansion?.slotBonus || 5);
totalStorageBonus += storageExpansion;
if (debugLogger) debugLogger.logStep('Upgrade bonuses calculated', {
storageExpansionLevel: this.upgrades.storage_expansion?.currentLevel || 0,
storageExpansionBonus: storageExpansion,
totalStorageBonusWithUpgrades: totalStorageBonus
});
const oldPower = this.base.power;
const oldMaxPower = this.base.maxPower;
const oldStorage = this.base.storage;
const oldMaxStorage = this.base.maxStorage;
this.base.power = Math.max(0, totalPowerCost);
this.base.maxPower = 100 + ((this.upgrades.power_efficiency?.currentLevel || 0) * 20);
// Update inventory base slots with storage upgrades
if (this.game.systems.inventory) {
const newBaseSlots = 30 + totalStorageBonus; // 30 base + storage bonuses
const oldBaseSlots = this.game.systems.inventory.baseMaxSlots;
this.game.systems.inventory.baseMaxSlots = newBaseSlots;
this.game.systems.inventory.updateStarbaseBonusSlots(this.game.systems.inventory.starbaseBonusSlots);
if (debugLogger) debugLogger.logStep('Inventory slots updated', {
oldBaseSlots: oldBaseSlots,
newBaseSlots: newBaseSlots,
starbaseBonusSlots: this.game.systems.inventory.starbaseBonusSlots,
finalMaxSlots: this.game.systems.inventory.maxSlots
});
}
// Keep legacy storage for backward compatibility
this.base.storage = 1000 + (totalStorageBonus * 100);
this.base.maxStorage = this.base.storage;
if (debugLogger) debugLogger.endStep('BaseSystem.calculateBaseStats', {
powerChanges: {
oldPower: oldPower,
newPower: this.base.power,
oldMaxPower: oldMaxPower,
newMaxPower: this.base.maxPower
},
storageChanges: {
oldStorage: oldStorage,
newStorage: this.base.storage,
oldMaxStorage: oldMaxStorage,
newMaxStorage: this.base.maxStorage
},
totals: {
totalPowerCost: totalPowerCost,
totalStorageBonus: totalStorageBonus,
storageExpansionBonus: storageExpansion
},
roomsProcessed: this.base.rooms.length
});
}
// Base upgrades
upgradeBase(upgradeId) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.upgradeBase', {
upgradeId: upgradeId,
currentCredits: this.game.systems.economy.credits,
currentUpgradeLevel: this.upgrades[upgradeId] ? this.upgrades[upgradeId].currentLevel : null
});
const upgrade = this.upgrades[upgradeId];
if (!upgrade) {
if (debugLogger) debugLogger.endStep('BaseSystem.upgradeBase', {
success: false,
reason: 'Upgrade not found',
upgradeId: upgradeId
});
return false;
}
if (debugLogger) debugLogger.logStep('Upgrade found', {
upgradeName: upgrade.name,
currentLevel: upgrade.currentLevel,
maxLevel: upgrade.maxLevel,
baseCost: upgrade.cost
});
if (upgrade.currentLevel >= upgrade.maxLevel) {
this.game.showNotification('Upgrade at maximum level', 'warning', 3000);
if (debugLogger) debugLogger.endStep('BaseSystem.upgradeBase', {
success: false,
reason: 'Upgrade at maximum level',
upgradeId: upgradeId,
currentLevel: upgrade.currentLevel,
maxLevel: upgrade.maxLevel
});
return false;
}
const cost = upgrade.cost * (upgrade.currentLevel + 1);
if (this.game.systems.economy.credits < cost) {
this.game.showNotification('Not enough credits', 'error', 3000);
if (debugLogger) debugLogger.endStep('BaseSystem.upgradeBase', {
success: false,
reason: 'Insufficient credits',
upgradeId: upgradeId,
requiredCredits: cost,
currentCredits: this.game.systems.economy.credits
});
return false;
}
if (debugLogger) debugLogger.logStep('Upgrade validation passed', {
upgradeId: upgradeId,
currentLevel: upgrade.currentLevel,
newLevel: upgrade.currentLevel + 1,
cost: cost
});
// Apply upgrade
const oldCredits = this.game.systems.economy.credits;
this.game.systems.economy.removeCredits(cost);
upgrade.currentLevel++;
if (debugLogger) debugLogger.logStep('Upgrade applied', {
oldCredits: oldCredits,
newCredits: this.game.systems.economy.credits,
costDeducted: cost,
newUpgradeLevel: upgrade.currentLevel
});
// Apply effects
let statsUpdated = false;
if (upgrade.effect.powerReduction) {
if (debugLogger) debugLogger.logStep('Applying power reduction effect');
this.updateBaseStats();
statsUpdated = true;
}
if (upgrade.effect.storageBonus) {
if (debugLogger) debugLogger.logStep('Applying storage bonus effect');
this.updateBaseStats();
statsUpdated = true;
}
if (upgrade.effect.productionBonus) {
if (debugLogger) debugLogger.logStep('Applying production bonus effect', {
productionBonus: upgrade.effect.productionBonus
});
statsUpdated = true;
}
if (upgrade.effect.defenseBonus) {
if (debugLogger) debugLogger.logStep('Applying defense bonus effect', {
defenseBonus: upgrade.effect.defenseBonus
});
statsUpdated = true;
}
if (!statsUpdated) {
// For upgrades like storage_expansion that don't have effects in the effect object
if (debugLogger) debugLogger.logStep('Updating base stats for upgrade');
this.updateBaseStats();
}
this.game.showNotification(`${upgrade.name} upgraded to level ${upgrade.currentLevel}!`, 'success', 3000);
if (debugLogger) debugLogger.endStep('BaseSystem.upgradeBase', {
success: true,
upgradeId: upgradeId,
upgradeName: upgrade.name,
oldLevel: upgrade.currentLevel - 1,
newLevel: upgrade.currentLevel,
cost: cost,
finalCredits: this.game.systems.economy.credits,
effectsApplied: statsUpdated
});
return true;
}
// Base production
update(deltaTime) {
if (this.game.state.paused) {
return;
}
// Auto production from automation systems
const autoProductionRate = (this.upgrades.automation_systems?.currentLevel || 0) * 0.05;
if (autoProductionRate > 0) {
// Use real computer time delta
const production = (deltaTime / 1000) * autoProductionRate * 10; // 10 credits per second per level
this.game.systems.economy.addCredits(Math.floor(production), 'base_production');
}
// Power generation from power generators
const powerGenerators = this.base.rooms.filter(r => r.type === 'power_generator');
if (powerGenerators.length > 0) {
const powerGeneration = powerGenerators.reduce((total, gen) => total + Math.abs(gen.powerCost), 0);
// Power generation would affect other systems
}
// Research production
if ((this.upgrades.research_lab?.currentLevel || 0) > 0) {
const researchRate = (this.upgrades.research_lab?.currentLevel || 0) * 0.1;
// Research points generation would go here
}
// Resource processing
if ((this.upgrades.resource_processor?.currentLevel || 0) > 0) {
const processingRate = (this.upgrades.resource_processor?.currentLevel || 0) * 0.05;
// Resource processing would go here
}
}
// UI updates
updateUI() {
this.setupBaseNavigation();
this.updateBaseDisplay();
this.updateUpgradesList();
this.updateShipGallery();
this.updateStarbaseList();
}
// Base Navigation
setupBaseNavigation() {
const navButtons = document.querySelectorAll('.base-nav-btn');
navButtons.forEach(button => {
button.addEventListener('click', () => {
const view = button.dataset.view;
this.switchBaseView(view);
});
});
}
switchBaseView(view) {
// Hide all views
document.querySelectorAll('.base-view-content').forEach(content => {
content.classList.add('hidden');
});
// Remove active class from all buttons
document.querySelectorAll('.base-nav-btn').forEach(btn => {
btn.classList.remove('active');
});
// Show selected view
const selectedView = document.getElementById(`base-${view}`);
if (selectedView) {
selectedView.classList.remove('hidden');
}
// Add active class to clicked button
const activeBtn = document.querySelector(`[data-view="${view}"]`);
if (activeBtn) {
activeBtn.classList.add('active');
}
// Initialize view-specific content
if (view === 'visualization') {
this.initializeBaseVisualization();
} else if (view === 'ships') {
this.updateShipGallery();
} else if (view === 'starbases') {
// Boot the isometric starbase world instead of the old list UI
if (typeof _startStarbaseWorld === 'function') {
_startStarbaseWorld();
}
} else {
// Leaving starbases — stop the world loop
if (typeof _stopStarbaseWorld === 'function') {
_stopStarbaseWorld();
}
}
}
// Base Visualization
initializeBaseVisualization() {
const canvas = document.getElementById('baseCanvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw base grid
this.drawBaseVisualization(ctx, canvas);
// Update info overlay
this.updateBaseInfoOverlay();
}
drawBaseVisualization(ctx, canvas) {
const cellSize = 40;
const offsetX = (canvas.width - this.gridSize * cellSize) / 2;
const offsetY = (canvas.height - this.gridSize * cellSize) / 2;
// Draw grid
ctx.strokeStyle = '#333';
ctx.lineWidth = 1;
for (let y = 0; y <= this.gridSize; y++) {
ctx.beginPath();
ctx.moveTo(offsetX, offsetY + y * cellSize);
ctx.lineTo(offsetX + this.gridSize * cellSize, offsetY + y * cellSize);
ctx.stroke();
}
for (let x = 0; x <= this.gridSize; x++) {
ctx.beginPath();
ctx.moveTo(offsetX + x * cellSize, offsetY);
ctx.lineTo(offsetX + x * cellSize, offsetY + this.gridSize * cellSize);
ctx.stroke();
}
// Draw rooms
for (let y = 0; y < this.gridSize; y++) {
for (let x = 0; x < this.gridSize; x++) {
const roomId = this.grid[y][x];
if (roomId) {
const room = this.base.rooms.find(r => r.id === roomId);
if (room) {
this.drawRoom(ctx, room, x, y, cellSize, offsetX, offsetY);
}
}
}
}
}
drawRoom(ctx, room, gridX, gridY, cellSize, offsetX, offsetY) {
const x = offsetX + gridX * cellSize;
const y = offsetY + gridY * cellSize;
// Room colors by type
const roomColors = {
command_center: '#4CAF50',
power_generator: '#FFC107',
defense_turret: '#F44336',
storage: '#2196F3',
research_lab: '#9C27B0',
factory: '#FF9800',
medical_bay: '#00BCD4',
hangar: '#795548'
};
const color = roomColors[room.type] || '#666';
// Draw room
ctx.fillStyle = color;
ctx.fillRect(x + 2, y + 2, cellSize - 4, cellSize - 4);
// Draw room border
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.strokeRect(x + 2, y + 2, cellSize - 4, cellSize - 4);
// Draw room icon/initial
ctx.fillStyle = '#fff';
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(room.name.charAt(0), x + cellSize/2, y + cellSize/2);
}
updateBaseInfoOverlay() {
const infoDisplay = document.getElementById('baseInfoDisplay');
if (!infoDisplay) return;
const totalRooms = this.base.rooms.length;
const powerUsage = this.base.power;
const maxPower = this.base.maxPower;
const storage = this.base.storage;
const maxStorage = this.base.maxStorage;
infoDisplay.innerHTML = `
<div class="base-info-item">
<strong>Base Name:</strong> ${this.base.name}
</div>
<div class="base-info-item">
<strong>Level:</strong> ${this.base.level}
</div>
<div class="base-info-item">
<strong>Total Rooms:</strong> ${totalRooms}
</div>
<div class="base-info-item">
<strong>Power Usage:</strong> ${powerUsage}/${maxPower}
</div>
<div class="base-info-item">
<strong>Storage:</strong> ${storage}/${maxStorage}
</div>
`;
}
// Ship Gallery
initializeShipGallery() {
// Initialize purchased ships array
if (!this.purchasedShips) {
this.purchasedShips = [];
// Add current ship to gallery with proper timestamp ID
const player = this.game.systems.player;
if (player.ship) {
this.purchasedShips.push({
id: Date.now().toString(), // Use timestamp instead of hardcoded 'current_ship'
name: player.ship.name || 'Default Ship',
class: player.ship.class || 'fighter',
level: player.ship.level || 1,
stats: { ...player.attributes },
isCurrent: true
});
}
}
}
updateShipGallery() {
const shipGrid = document.getElementById('shipGrid');
if (!shipGrid) {
return;
}
if (!this.purchasedShips) {
this.initializeShipGallery();
}
if (!this.purchasedShips || this.purchasedShips.length === 0) {
shipGrid.innerHTML = '<p>No ships purchased yet. Visit the shop to buy ships!</p>';
return;
}
shipGrid.innerHTML = '';
this.purchasedShips.forEach(ship => {
const shipElement = document.createElement('div');
shipElement.className = `ship-card ${ship.isCurrent ? 'current-ship' : ''}`;
shipElement.innerHTML = `
<div class="ship-header">
<h4>${ship.name}</h4>
${ship.isCurrent ? '<span class="current-badge">Current</span>' : ''}
</div>
<div class="ship-info">
<div class="ship-class">Class: ${ship.class}</div>
<div class="ship-level">Level: ${ship.level}</div>
</div>
<div class="ship-stats">
<div class="stat">Attack: ${ship.stats.attack || 0}</div>
<div class="stat">Speed: ${ship.stats.speed || 0}</div>
<div class="stat">Defense: ${ship.stats.defense || 0}</div>
</div>
${!ship.isCurrent ? `
<button class="btn btn-primary equip-ship-btn" onclick="window.game.systems.baseSystem.equipShip('${ship.id}')">Equip Ship</button>
` : ''}
`;
shipGrid.appendChild(shipElement);
});
}
equipShip(shipId) {
const ship = this.purchasedShips.find(s => s.id === shipId);
if (!ship) {
this.game.showNotification('Ship not found!', 'error', 3000);
return;
}
if (ship.isCurrent) {
this.game.showNotification('This ship is already equipped!', 'info', 3000);
return;
}
const player = this.game.systems.player;
player.ship.name = ship.name;
player.ship.class = ship.class;
player.ship.level = ship.level;
player.ship.texture = ship.texture; // Copy texture for image display
// Apply ship-specific stats (not just attributes)
if (ship.stats.health || ship.stats.hull) {
const healthValue = ship.stats.health || ship.stats.hull || 100;
player.ship.maxHealth = healthValue;
player.ship.health = healthValue;
}
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
player.attributes.attack = ship.stats.attack || player.attributes.attack;
player.attributes.speed = ship.stats.speed || player.attributes.speed;
player.attributes.defense = ship.stats.defense || player.attributes.defense;
if (ship.stats.health || ship.stats.hull) {
const healthValue = ship.stats.health || ship.stats.hull || 100;
player.attributes.maxHealth = healthValue;
player.attributes.health = healthValue;
}
// Update isCurrent flags
this.purchasedShips.forEach(s => s.isCurrent = false);
ship.isCurrent = true;
// Force immediate gallery update after flag changes
const shipGrid = document.getElementById('shipGrid');
if (shipGrid) {
shipGrid.innerHTML = '';
this.updateShipGallery();
}
// Also update ShipSystem's currentShip to keep systems synchronized
if (this.game.systems.ship) {
// Find or create a ship object in ShipSystem format
const shipSystemShip = {
id: ship.id,
name: ship.name,
image: ship.texture || `assets/textures/ships/${ship.class.toLowerCase()}.png`,
class: ship.class,
level: ship.level,
stats: ship.stats,
status: 'active',
rarity: 'Common'
};
// Update ShipSystem's current ship
this.game.systems.ship.currentShip = shipSystemShip;
// Update the ships array in ShipSystem if needed
const existingShipIndex = this.game.systems.ship.ships.findIndex(s => s.id === ship.id);
if (existingShipIndex >= 0) {
this.game.systems.ship.ships[existingShipIndex].status = 'active';
this.game.systems.ship.ships[existingShipIndex] = shipSystemShip;
} else {
// Add to ships array if not present
this.game.systems.ship.ships.push(shipSystemShip);
}
// Mark all other ships as inactive
this.game.systems.ship.ships.forEach(s => {
if (s.id !== ship.id) s.status = 'inactive';
});
}
// Only update player UI if in multiplayer mode or game is actively running
if (this.game.shouldUpdateGUI()) {
player.updateUI();
}
this.updateShipGallery();
// Also update ShipSystem display
if (this.game.systems.ship && this.game.systems.ship.updateCurrentShipDisplay) {
this.game.systems.ship.updateCurrentShipDisplay();
} else {
this.updateCurrentShipDisplayDirect();
}
// Force save the game to persist the ship change
this.game.save().then(() => {
// Force complete gallery refresh after save completes
const shipGrid = document.getElementById('shipGrid');
if (shipGrid) {
shipGrid.innerHTML = ''; // Clear completely
this.updateShipGallery(); // Rebuild from scratch
}
// Force ShipSystem to sync with new current ship
if (this.game.systems.ship && this.game.systems.ship.syncWithPlayerShip) {
this.game.systems.ship.syncWithPlayerShip();
}
});
this.game.showNotification(`Equipped ${ship.name}!`, 'success', 3000);
}
updateCurrentShipDisplayDirect() {
const player = this.game.systems.player;
if (!player || !player.ship) {
return;
}
const elements = {
currentShipImage: document.getElementById('currentShipImage'),
currentShipName: document.getElementById('currentShipName'),
currentShipClass: document.getElementById('currentShipClass'),
currentShipLevel: document.getElementById('currentShipLevel'),
currentShipHealth: document.getElementById('currentShipHealth'),
currentShipAttack: document.getElementById('currentShipAttack'),
currentShipDefense: document.getElementById('currentShipDefense'),
currentShipSpeed: document.getElementById('currentShipSpeed')
};
const ship = player.ship;
if (elements.currentShipImage) {
const imagePath = ship.texture || `assets/textures/ships/${ship.class.toLowerCase()}.png`;
console.log(`[DEBUG] Ship image path: ${imagePath}, texture: ${ship.texture}, class: ${ship.class}`);
elements.currentShipImage.src = imagePath;
// Add error handling for image loading
elements.currentShipImage.onerror = function() {
console.error(`[DEBUG] Failed to load image: ${imagePath}`);
// Fallback to default ship image
this.src = 'assets/textures/ships/starter_cruiser.png';
};
}
if (elements.currentShipName) elements.currentShipName.textContent = ship.name || 'Unknown Ship';
if (elements.currentShipClass) elements.currentShipClass.textContent = ship.class || 'Unknown';
if (elements.currentShipLevel) elements.currentShipLevel.textContent = ship.level || 1;
if (elements.currentShipHealth) elements.currentShipHealth.textContent = `${ship.health}/${ship.maxHealth}`;
if (elements.currentShipAttack) elements.currentShipAttack.textContent = ship.attack || 0;
if (elements.currentShipDefense) elements.currentShipDefense.textContent = ship.defense || ship.defence || 0;
if (elements.currentShipSpeed) elements.currentShipSpeed.textContent = ship.speed || 0;
}
addShipToGallery(shipData) {
this.purchasedShips.push({
id: Date.now().toString(),
name: shipData.name,
class: shipData.type,
level: 1,
stats: { ...shipData.stats },
texture: shipData.texture, // Include texture for image display
isCurrent: false
});
this.updateShipGallery();
}
// Starbase System
initializeStarbaseSystem() {
// Initialize starbase collection
if (!this.starbases) {
this.starbases = [
{
id: 'main_base',
name: 'Command Center Alpha',
type: 'command',
level: 1,
location: 'Sector Alpha-1',
isMain: true
}
];
}
// Define available starbase types
this.starbaseTypes = {
mining: {
name: 'Mining Outpost',
description: 'Automated mining facility for resource extraction',
price: 25000,
currency: 'credits',
icon: 'fa-hammer',
benefits: ['+500 credits/hour', '+50 gems/day', '+5 inventory slots'],
requiredLevel: 5,
inventoryBonus: 5
},
research: {
name: 'Research Station',
description: 'Advanced research facility for technology development',
price: 50000,
currency: 'credits',
icon: 'fa-flask',
benefits: ['+100 experience/hour', '+10% skill progression', '+10 inventory slots'],
requiredLevel: 10,
inventoryBonus: 10
},
defense: {
name: 'Defense Platform',
description: 'Military installation for sector protection',
price: 40000,
currency: 'credits',
icon: 'fa-shield-alt',
benefits: ['+15% combat effectiveness', '+100 defense rating'],
requiredLevel: 8,
inventoryBonus: 8
},
trading: {
name: 'Trading Hub',
description: 'Commercial center for trade and commerce',
price: 75000,
currency: 'credits',
icon: 'fa-store',
benefits: ['+20% shop discounts', '+200 gems/day'],
requiredLevel: 12,
inventoryBonus: 15
}
};
}
updateStarbaseList() {
const starbaseList = document.getElementById('starbaseList');
if (!starbaseList) return;
starbaseList.innerHTML = '';
// Initialize starbases if not exists
if (!this.starbases) {
this.initializeStarbaseSystem();
}
if (!this.starbases || this.starbases.length === 0) {
starbaseList.innerHTML = '<p>No starbases owned yet.</p>';
return;
}
this.starbases.forEach(starbase => {
const starbaseElement = document.createElement('div');
starbaseElement.className = `starbase-card ${starbase.isMain ? 'main-starbase' : ''}`;
starbaseElement.innerHTML = `
<div class="starbase-header">
<h4>${starbase.name}</h4>
${starbase.isMain ? '<span class="main-badge">Main Base</span>' : ''}
</div>
<div class="starbase-info">
<div class="starbase-type">Type: ${starbase.type}</div>
<div class="starbase-level">Level: ${starbase.level}</div>
<div class="starbase-location">Location: ${starbase.location}</div>
</div>
<div class="starbase-actions">
<button class="btn btn-secondary" onclick="if(window.game && window.game.systems) window.game.systems.baseSystem.viewStarbaseDetails('${starbase.id}')">
View Details
</button>
${!starbase.isMain ? `
<button class="btn btn-danger" onclick="if(window.game && window.game.systems) window.game.systems.baseSystem.decommissionStarbase('${starbase.id}')">
Decommission
</button>
` : ''}
</div>
`;
starbaseList.appendChild(starbaseElement);
});
}
updateStarbasePurchaseList() {
const purchaseList = document.getElementById('starbasePurchaseItems');
if (!purchaseList) return;
purchaseList.innerHTML = '';
const player = this.game.systems.player;
Object.entries(this.starbaseTypes).forEach(([type, starbase]) => {
const starbaseElement = document.createElement('div');
starbaseElement.className = 'starbase-purchase-card';
const canAfford = this.game.systems.economy.credits >= starbase.price;
const meetsLevel = player.stats.level >= starbase.requiredLevel;
const canPurchase = canAfford && meetsLevel;
starbaseElement.innerHTML = `
<div class="starbase-purchase-header">
<h4>${starbase.name}</h4>
<div class="starbase-icon">
<i class="fas ${starbase.icon}"></i>
</div>
</div>
<div class="starbase-purchase-description">
${starbase.description}
</div>
<div class="starbase-purchase-benefits">
<h5>Benefits:</h5>
<ul>
${starbase.benefits.map(benefit => `<li>${benefit}</li>`).join('')}
</ul>
</div>
<div class="starbase-purchase-requirements">
<div class="requirement">
<strong>Cost:</strong> ${starbase.price} ${starbase.currency}
</div>
<div class="requirement">
<strong>Required Level:</strong> ${starbase.requiredLevel}
</div>
</div>
<div class="starbase-purchase-actions">
<button class="btn btn-primary ${!canPurchase ? 'disabled' : ''}"
onclick="if(window.game && window.game.systems) window.game.systems.baseSystem.purchaseStarbase('${type}')"
${!canPurchase ? 'disabled' : ''}>
${!meetsLevel ? 'Level Required' : !canAfford ? 'Insufficient Funds' : 'Purchase Starbase'}
</button>
</div>
`;
purchaseList.appendChild(starbaseElement);
});
}
purchaseStarbase(type) {
const starbaseTemplate = this.starbaseTypes[type];
if (!starbaseTemplate) {
this.game.showNotification('Invalid starbase type!', 'error', 3000);
return;
}
const player = this.game.systems.player;
const economy = this.game.systems.economy;
// Check requirements
if (player.stats.level < starbaseTemplate.requiredLevel) {
this.game.showNotification(`Level ${starbaseTemplate.requiredLevel} required!`, 'error', 3000);
return;
}
if (economy.credits < starbaseTemplate.price) {
this.game.showNotification('Insufficient credits!', 'error', 3000);
return;
}
// Process payment
economy.credits -= starbaseTemplate.price;
// Create new starbase
const newStarbase = {
id: Date.now().toString(),
name: `${starbaseTemplate.name} ${this.starbases.length}`,
type: type,
level: 1,
location: `Sector ${String.fromCharCode(65 + this.starbases.length)}-${this.starbases.length}`,
isMain: false,
benefits: starbaseTemplate.benefits
};
this.starbases.push(newStarbase);
// Apply benefits
this.applyStarbaseBenefits(newStarbase);
// Only update UI if in multiplayer mode or game is actively running
if (this.game.shouldUpdateGUI()) {
economy.updateUI();
this.updateStarbaseList();
this.updateStarbasePurchaseList();
}
this.game.showNotification(`Purchased ${starbaseTemplate.name}!`, 'success', 4000);
}
applyStarbaseBenefits(starbase) {
// Apply passive benefits based on starbase type
switch (starbase.type) {
case 'mining':
// Start passive credit generation
this.startMiningProduction(starbase);
// Apply inventory bonus
this.updateInventoryBonusSlots();
break;
case 'research':
// Apply experience bonus
this.game.systems.player.experienceMultiplier = (this.game.systems.player.experienceMultiplier || 1) + 0.1;
// Apply inventory bonus
this.updateInventoryBonusSlots();
break;
case 'defense':
// Apply defense bonus
this.game.systems.player.attributes.defense = Math.floor(this.game.systems.player.attributes.defense * 1.2);
// Apply inventory bonus
this.updateInventoryBonusSlots();
break;
case 'trading':
// Apply shop discount
this.game.systems.economy.discountMultiplier = 0.85;
// Apply inventory bonus
this.updateInventoryBonusSlots();
break;
}
}
updateInventoryBonusSlots() {
// Calculate total inventory bonus from all owned starbases
let totalBonusSlots = 0;
if (this.starbases && this.starbases.length > 0) {
this.starbases.forEach(starbase => {
const starbaseType = this.starbaseTypes[starbase.type];
if (starbaseType && starbaseType.inventoryBonus) {
totalBonusSlots += starbaseType.inventoryBonus;
}
});
}
// Update inventory system with new bonus slots
if (this.game.systems.inventory) {
this.game.systems.inventory.updateStarbaseBonusSlots(totalBonusSlots);
}
}
startMiningProduction(starbase) {
// Only start mining if in multiplayer mode or game is actively running
const shouldStartMining = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
if (shouldStartMining && !this.miningInterval) {
// Set up passive credit generation
this.miningInterval = setInterval(() => {
this.game.systems.economy.addCredits(500, 'mining_outpost');
}, 3600000); // 1 hour
console.log('[BASE SYSTEM] Mining production started');
} else if (!shouldStartMining) {
console.log('[BASE SYSTEM] Skipping mining production - not in multiplayer mode');
}
}
viewStarbaseDetails(starbaseId) {
const starbase = this.starbases.find(s => s.id === starbaseId);
if (!starbase) return;
const details = `
<h3>${starbase.name}</h3>
<p><strong>Type:</strong> ${starbase.type}</p>
<p><strong>Level:</strong> ${starbase.level}</p>
<p><strong>Location:</strong> ${starbase.location}</p>
${starbase.benefits ? `
<h4>Active Benefits:</h4>
<ul>
${starbase.benefits.map(benefit => `<li>${benefit}</li>`).join('')}
</ul>
` : ''}
`;
if (this.game && this.game.systems && this.game.systems.ui) {
this.game.systems.ui.showModal('Starbase Details', details);
}
}
decommissionStarbase(starbaseId) {
if (!confirm('Are you sure you want to decommission this starbase? You will receive 50% of its value back.')) {
return;
}
const starbaseIndex = this.starbases.findIndex(s => s.id === starbaseId);
if (starbaseIndex === -1) return;
const starbase = this.starbases[starbaseIndex];
const refundAmount = Math.floor(this.starbaseTypes[starbase.type].price * 0.5);
// Remove starbase
this.starbases.splice(starbaseIndex, 1);
// Give refund
this.game.systems.economy.addCredits(refundAmount, 'starbase_refund');
// Update inventory bonus slots after removal
this.updateInventoryBonusSlots();
// Update UI
this.updateStarbaseList();
this.updateStarbasePurchaseList();
this.game.showNotification(`Decommissioned ${starbase.name}. Refunded ${refundAmount} credits.`, 'info', 4000);
}
updateBaseDisplay() {
const baseRoomsElement = document.getElementById('baseRooms');
if (!baseRoomsElement) return;
// Ensure grid is initialized
if (!this.grid || this.grid.length === 0) {
console.log('[BASE SYSTEM] Grid not initialized, initializing now');
this.initializeGrid();
}
baseRoomsElement.innerHTML = '';
// Create grid container
const gridContainer = document.createElement('div');
gridContainer.className = 'base-grid';
// Create grid cells
for (let y = 0; y < this.gridSize; y++) {
for (let x = 0; x < this.gridSize; x++) {
const cell = document.createElement('div');
cell.className = 'base-cell';
cell.dataset.x = x;
cell.dataset.y = y;
const room = this.getRoomAt(x, y);
if (room) {
// Check if this is the main cell of the room
if (room.x === x && room.y === y) {
cell.className += ` room ${room.type}`;
cell.innerHTML = `
<div class="room-icon">
<i class="fas ${this.roomTypes[room.type].icon}"></i>
</div>
<div class="room-level">L${room.level}</div>
`;
cell.title = `${room.name} (Level ${room.level})`;
} else {
// Other cells of the same room are just filled
cell.innerHTML = '';
}
cell.title = `${room.name} (Level ${room.level})`;
} else {
cell.className += ' empty';
cell.addEventListener('click', () => this.showRoomBuildMenu(x, y));
}
gridContainer.appendChild(cell);
}
}
baseRoomsElement.appendChild(gridContainer);
// Add base info with connected inventory storage
const baseInfo = document.createElement('div');
baseInfo.className = 'base-info';
// Get inventory information
const inventoryInfo = this.game.systems.inventory ? this.game.systems.inventory.getInventoryInfo() : null;
baseInfo.innerHTML = `
<h3>${this.base.name}</h3>
<div class="base-stats">
<div>Level: ${this.base.level}</div>
<div>Power: ${this.base.power}/${this.base.maxPower}</div>
<div>Storage: ${inventoryInfo ? `${inventoryInfo.currentSlots}/${inventoryInfo.totalMaxSlots}` : `${this.base.storage}/${this.base.maxStorage}`} slots</div>
${inventoryInfo ? `<div class="storage-breakdown">
<small>Base: ${inventoryInfo.baseMaxSlots} | Starbase Bonus: +${inventoryInfo.starbaseBonusSlots}</small>
</div>` : ''}
</div>
`;
baseRoomsElement.appendChild(baseInfo);
}
updateUpgradesList() {
const upgradesElement = document.getElementById('baseUpgrades');
if (!upgradesElement) return;
upgradesElement.innerHTML = '';
Object.entries(this.upgrades).forEach(([upgradeId, upgrade]) => {
const upgradeElement = document.createElement('div');
upgradeElement.className = 'upgrade-item';
const cost = upgrade.cost * (upgrade.currentLevel + 1);
const canAfford = this.game.systems.economy.credits >= cost;
const isMaxLevel = upgrade.currentLevel >= upgrade.maxLevel;
upgradeElement.innerHTML = `
<div class="upgrade-name">${upgrade.name}</div>
<div class="upgrade-description">${upgrade.description}</div>
<div class="upgrade-level">Level: ${upgrade.currentLevel}/${upgrade.maxLevel}</div>
<div class="upgrade-cost">Cost: ${cost} credits</div>
<button class="btn btn-primary ${!canAfford || isMaxLevel ? 'disabled' : ''}"
onclick="if(window.game && window.game.systems) window.game.systems.baseSystem.upgradeBase('${upgradeId}')"
${!canAfford || isMaxLevel ? 'disabled' : ''}>
${isMaxLevel ? 'MAX' : 'Upgrade'}
</button>
`;
upgradesElement.appendChild(upgradeElement);
});
}
showRoomBuildMenu(x, y) {
const availableRooms = Object.entries(this.roomTypes)
.filter(([roomType, template]) =>
template.requiredLevel <= this.game.systems.player.stats.level
)
.filter(([roomType, template]) => this.canBuildRoom(roomType, x, y));
if (availableRooms.length === 0) {
this.game.showNotification('No rooms available for this location', 'warning', 3000);
return;
}
let menuContent = '<h3>Build Room</h3><div class="room-options">';
availableRooms.forEach(([roomType, template]) => {
const roomSize = this.getRoomSize(template.size);
menuContent += `
<div class="room-option" onclick="if(window.game && window.game.systems) window.game.systems.baseSystem.addRoom('${roomType}', ${x}, ${y})">
<div class="room-option-header">
<i class="fas ${template.icon}"></i>
<span>${template.name}</span>
</div>
<div class="room-option-info">
<div>Size: ${roomSize.width}x${roomSize.height}</div>
<div>Power: ${template.powerCost > 0 ? '+' : ''}${template.powerCost}</div>
<div>Cost: ${template.buildCost} credits</div>
</div>
<div class="room-option-description">${template.description}</div>
</div>
`;
});
menuContent += '</div>';
}
updateUpgradesList() {
const upgradesElement = document.getElementById('baseUpgrades');
if (!upgradesElement) return;
upgradesElement.innerHTML = '';
Object.entries(this.upgrades).forEach(([upgradeId, upgrade]) => {
const upgradeElement = document.createElement('div');
upgradeElement.className = 'upgrade-item';
const cost = upgrade.cost * (upgrade.currentLevel + 1);
const canAfford = this.game.systems.economy.credits >= cost;
const isMaxLevel = upgrade.currentLevel >= upgrade.maxLevel;
upgradeElement.innerHTML = `
<div class="upgrade-name">${upgrade.name}</div>
<div class="upgrade-description">${upgrade.description}</div>
<div class="upgrade-level">Level: ${upgrade.currentLevel}/${upgrade.maxLevel}</div>
<div class="upgrade-cost">Cost: ${cost} credits</div>
<button class="btn btn-primary ${!canAfford || isMaxLevel ? 'disabled' : ''}"
onclick="if(window.game && window.game.systems) window.game.systems.baseSystem.upgradeBase('${upgradeId}')"
${!canAfford || isMaxLevel ? 'disabled' : ''}>
${isMaxLevel ? 'MAX' : 'Upgrade'}
</button>
`;
upgradesElement.appendChild(upgradeElement);
});
}
showRoomBuildMenu(x, y) {
const availableRooms = Object.entries(this.roomTypes)
.filter(([roomType, template]) =>
template.requiredLevel <= this.game.systems.player.stats.level
)
.filter(([roomType, template]) => this.canBuildRoom(roomType, x, y));
if (availableRooms.length === 0) {
this.game.showNotification('No rooms available for this location', 'warning', 3000);
return;
}
let menuContent = '<h3>Build Room</h3><div class="room-options">';
availableRooms.forEach(([roomType, template]) => {
const roomSize = this.getRoomSize(template.size);
menuContent += `
<div class="room-option" onclick="if(window.game && window.game.systems) window.game.systems.baseSystem.addRoom('${roomType}', ${x}, ${y})">
<div class="room-option-header">
<i class="fas ${template.icon}"></i>
<span>${template.name}</span>
</div>
<div class="room-option-info">
<div>Size: ${roomSize.width}x${roomSize.height}</div>
<div>Power: ${template.powerCost > 0 ? '+' : ''}${template.powerCost}</div>
<div>Cost: ${template.buildCost} credits</div>
</div>
<div class="room-option-description">${template.description}</div>
</div>
`;
});
menuContent += '</div>';
if (this.game && this.game.systems && this.game.systems.ui) {
this.game.systems.ui.showModal('Build Room', menuContent);
}
}
// Save/Load
save() {
const debugLogger = window.debugLogger;
// if (debugLogger) debugLogger.startStep('BaseSystem.save', {
// baseName: this.base.name,
// baseLevel: this.base.level,
// roomsCount: this.base.rooms.length,
// gridCellsCount: this.grid.flat().filter(cell => cell !== null).length,
// upgradesCount: Object.keys(this.upgrades).length
// });
const saveData = {
base: this.base,
grid: this.grid,
upgrades: this.upgrades,
purchasedShips: this.purchasedShips || []
};
// if (debugLogger) debugLogger.endStep('BaseSystem.save', {
// saveDataSize: JSON.stringify(saveData).length,
// baseSaved: {
// name: saveData.base.name,
// level: saveData.base.level,
// roomsCount: saveData.base.rooms.length
// },
// gridSaved: {
// gridSize: this.gridSize,
// occupiedCells: this.grid.flat().filter(cell => cell !== null).length
// },
// upgradesSaved: Object.keys(saveData.upgrades).map(id => ({
// id: id,
// currentLevel: saveData.upgrades[id].currentLevel
// })),
// saveData: saveData
// });
return saveData;
}
load(data) {
const debugLogger = window.debugLogger;
const oldState = {
baseName: this.base.name,
baseLevel: this.base.level,
roomsCount: this.base.rooms.length,
upgradesState: Object.keys(this.upgrades).map(id => ({
id: id,
currentLevel: this.upgrades[id].currentLevel
}))
};
if (debugLogger) debugLogger.startStep('BaseSystem.load', {
oldState: oldState,
loadData: data
});
try {
if (data.base) {
const oldBase = { ...this.base };
this.base = { ...this.base, ...data.base };
if (debugLogger) debugLogger.logStep('Base data loaded', {
oldBase: {
name: oldBase.name,
level: oldBase.level,
roomsCount: oldBase.rooms.length
},
newBase: {
name: this.base.name,
level: this.base.level,
roomsCount: this.base.rooms.length
}
});
}
if (data.grid) {
const oldGrid = this.grid;
this.grid = data.grid;
if (debugLogger) debugLogger.logStep('Grid data loaded', {
oldGridCells: oldGrid.flat().filter(cell => cell !== null).length,
newGridCells: this.grid.flat().filter(cell => cell !== null).length,
gridSize: this.gridSize
});
}
if (data.upgrades) {
const oldUpgrades = Object.keys(this.upgrades).map(id => ({
id: id,
currentLevel: this.upgrades[id].currentLevel
}));
Object.entries(data.upgrades).forEach(([upgradeId, upgrade]) => {
if (this.upgrades[upgradeId]) {
Object.assign(this.upgrades[upgradeId], upgrade);
if (debugLogger) debugLogger.logStep('Upgrade loaded', {
upgradeId: upgradeId,
oldLevel: oldUpgrades.find(u => u.id === upgradeId)?.currentLevel || 0,
newLevel: this.upgrades[upgradeId].currentLevel
});
}
});
}
// Load purchased ships
if (data.purchasedShips) {
this.purchasedShips = data.purchasedShips;
if (debugLogger) debugLogger.logStep('Purchased ships loaded', {
shipsCount: this.purchasedShips.length,
shipNames: this.purchasedShips.map(s => s.name)
});
// Find the current ship from gallery and sync with player
const currentGalleryShip = this.purchasedShips.find(s => s.isCurrent);
if (currentGalleryShip) {
const player = this.game.systems.player;
if (player && player.ship) {
console.log('[DEBUG] Syncing player ship with gallery current ship:', currentGalleryShip);
// Update player's ship with gallery current ship data
player.ship.name = currentGalleryShip.name;
player.ship.class = currentGalleryShip.class;
player.ship.level = currentGalleryShip.level;
player.ship.texture = currentGalleryShip.texture;
// Update player attributes with ship stats
if (currentGalleryShip.stats) {
Object.assign(player.attributes, currentGalleryShip.stats);
// Update player.ship properties for consistency
player.ship.health = currentGalleryShip.stats.health || currentGalleryShip.stats.hull || 100;
player.ship.maxHealth = currentGalleryShip.stats.maxHealth || currentGalleryShip.stats.hull || 100;
player.ship.attack = currentGalleryShip.stats.attack || 10;
player.ship.defence = currentGalleryShip.stats.defense || currentGalleryShip.stats.defence || 5;
player.ship.speed = currentGalleryShip.stats.speed || 10;
}
console.log('[DEBUG] Player ship updated:', player.ship);
console.log('[DEBUG] Player attributes updated:', player.attributes);
// Only update UI displays if in multiplayer mode or game is actively running
if (this.game.shouldUpdateGUI()) {
player.updateUI();
if (this.game.systems.ship && this.game.systems.ship.updateCurrentShipDisplay) {
this.game.systems.ship.updateCurrentShipDisplay();
}
}
}
}
}
// Update base stats after loading
if (debugLogger) debugLogger.logStep('Updating base stats after load');
this.updateBaseStats();
// Update ship gallery after loading ships
if (data.purchasedShips) {
this.updateShipGallery();
}
if (debugLogger) debugLogger.endStep('BaseSystem.load', {
success: true,
oldState: oldState,
newState: {
baseName: this.base.name,
baseLevel: this.base.level,
roomsCount: this.base.rooms.length,
upgradesState: Object.keys(this.upgrades).map(id => ({
id: id,
currentLevel: this.upgrades[id].currentLevel
}))
},
baseStatsUpdated: true
});
} catch (error) {
if (debugLogger) debugLogger.errorEvent('BaseSystem.load', error, {
oldState: oldState,
loadData: data,
error: error.message
});
throw error;
}
}
reset() {
this.baseLevel = 1;
this.rooms = {};
// Reset upgrades to initial state
this.upgrades = {
power_efficiency: {
name: 'Power Efficiency',
description: 'Reduces power consumption of all rooms',
cost: 1000,
effect: { powerReduction: 0.1 },
maxLevel: 5,
currentLevel: 0
},
storage_expansion: {
name: 'Storage Expansion',
description: 'Increases inventory slots for item storage',
cost: 5000,
maxLevel: 10,
currentLevel: 0,
icon: 'fa-boxes',
slotBonus: 5
},
automation_systems: {
name: 'Automation Systems',
description: 'Automated resource generation and processing',
cost: 2500,
maxLevel: 10,
currentLevel: 0,
icon: 'fa-robot'
}
};
this.resources = {
energy: 100,
materials: 50,
research: 0
};
this.initializeBaseData();
}
updateBaseStats() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('BaseSystem.updateBaseStats');
// Calculate total power and storage from rooms
let totalPower = 0;
let totalStorage = 0;
this.base.rooms.forEach(room => {
const roomTemplate = this.roomTypes[room.type];
if (roomTemplate) {
totalPower += roomTemplate.powerGeneration || 0;
totalStorage += roomTemplate.storageBonus || 0;
}
});
// Update base stats
this.base.power = Math.max(0, totalPower);
this.base.maxStorage = 1000 + totalStorage;
// Update UI if available
if (this.game.systems.ui && this.game.systems.ui.updateBaseSystem) {
this.game.systems.ui.updateBaseSystem();
}
// if (debugLogger) debugLogger.endStep('BaseSystem.updateBaseStats', {
// totalPower: this.base.power,
// maxStorage: this.base.maxStorage,
// roomsCount: this.base.rooms.length
// });
}
}