/** * Galaxy Strike Online - Galaxy System * Procedurally generates and manages sectors */ class GalaxySystem { constructor() { this.GRID_W = 30; this.GRID_H = 20; this.TYPES = ['empty','empty','empty','asteroid','asteroid','trade_hub','npc_territory','ruins','void_rift']; this._sectors = null; } /** Lazy-generate the galaxy grid once */ getSectors() { if (!this._sectors) this._sectors = this._generate(); return this._sectors; } _generate() { const sectors = {}; for (let y = 0; y < this.GRID_H; y++) { for (let x = 0; x < this.GRID_W; x++) { const id = `${x}_${y}`; const distFromCenter = Math.hypot(x - 15, y - 10) / 18; // Richer regions further from core const typeRoll = Math.random(); let type = 'empty'; if (typeRoll > 0.70) type = 'asteroid'; if (typeRoll > 0.88) type = 'npc_territory'; if (typeRoll > 0.94) type = 'trade_hub'; if (typeRoll > 0.97 && distFromCenter > 0.4) type = 'ruins'; if (typeRoll > 0.99 && distFromCenter > 0.6) type = 'void_rift'; const richness = Math.random(); sectors[id] = { id, x, y, type, name: this._sectorName(x, y), threat: Math.min(10, Math.floor(distFromCenter * 10 + Math.random() * 3)), richness: type === 'asteroid' ? (0.3 + richness * 0.7) : 0, owner: null, explored: false, }; } } // Always make start sector safe sectors['15_10'] = { id:'15_10', x:15, y:10, type:'trade_hub', name:'New Haven', threat:0, richness:0, owner:null, explored:true }; return sectors; } _sectorName(x, y) { const prefixes = ['Alpha','Beta','Gamma','Delta','Sigma','Tau','Zeta','Omega','Nova','Vega','Lyra','Cygni']; const suffixes = ['Prime','Station','Reach','Deep','Expanse','Crossing','Rift','Gate','Void','Fields']; const seed = (x * 31 + y * 17) % (prefixes.length * suffixes.length); return prefixes[seed % prefixes.length] + ' ' + suffixes[Math.floor(seed / prefixes.length) % suffixes.length]; } /** Returns the sector or null */ getSector(id) { return this.getSectors()[id] || null; } /** Mark a sector explored by a player */ exploreSector(sectorId, playerData) { const sector = this.getSector(sectorId); if (!sector) throw new Error('Sector not found'); const explored = playerData.exploredSectors || []; if (!explored.includes(sectorId)) { explored.push(sectorId); playerData.exploredSectors = explored; } return sector; } /** Return list of sectors visible to player (explored + adjacent) */ getVisibleSectors(playerData) { const explored = new Set(playerData.exploredSectors || ['15_10']); const visible = new Set(explored); // Reveal adjacent sectors to all explored for (const id of explored) { const [x, y] = id.split('_').map(Number); for (const [dx, dy] of [[-1,0],[1,0],[0,-1],[0,1]]) { const nid = `${x+dx}_${y+dy}`; if (this.getSector(nid)) visible.add(nid); } } const all = this.getSectors(); return Array.from(visible).map(id => { const s = { ...all[id] }; s.explored = explored.has(id); if (!s.explored) { // Fog: hide details of unexplored but adjacent sectors s.name = '???'; s.richness = 0; s.owner = null; } return s; }); } /** Player claims/sets base in a sector */ claimSector(sectorId, playerData) { const sector = this.getSector(sectorId); if (!sector) throw new Error('Sector not found'); if (sector.type === 'void_rift') throw new Error('Cannot claim Void Rift sectors'); sector.owner = playerData.userId; this.exploreSector(sectorId, playerData); playerData.homeSector = sectorId; return sector; } } module.exports = GalaxySystem;